WEB-20367 JSON Schema: provide validation warning for the file under several schemas
authorirengrig <Irina.Chernushina@jetbrains.com>
Thu, 18 Feb 2016 14:13:28 +0000 (15:13 +0100)
committerirengrig <Irina.Chernushina@jetbrains.com>
Thu, 18 Feb 2016 16:01:00 +0000 (17:01 +0100)
Use all schemas available for the file; display warning message with quick fix to edit settings

json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsConfigurable.java
json/src/com/jetbrains/jsonSchema/extension/JsonSchemaFileProvider.java
json/src/com/jetbrains/jsonSchema/extension/JsonSchemaImportedProviderFactory.java
json/src/com/jetbrains/jsonSchema/extension/JsonSchemaSelfProviderFactory.java
json/src/com/jetbrains/jsonSchema/impl/CodeInsightProviders.java [new file with mode: 0644]
json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObjectCodeInsightWrapper.java
json/src/com/jetbrains/jsonSchema/impl/JsonSchemaServiceImpl.java
json/tests/test/com/jetbrains/jsonSchema/JsonSchemaTestProvider.java

index 80d2c957ea5cd96d08a9fc08daad45c645ac8969..8380614c750058a1c11bdd6c98eb61cbb23b0a7e 100644 (file)
@@ -1,5 +1,6 @@
 package com.jetbrains.jsonSchema;
 
+import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
 import com.intellij.json.JsonBundle;
 import com.intellij.openapi.Disposable;
 import com.intellij.openapi.actionSystem.AnAction;
@@ -185,6 +186,7 @@ public class JsonSchemaMappingsConfigurable extends MasterDetailsComponent imple
       final JsonSchemaService service = JsonSchemaService.Impl.get(project);
       if (service != null) service.reset();
     }
+    if (myProject != null) DaemonCodeAnalyzer.getInstance(myProject).restart();
   }
 
   private static void validate(@NotNull List<JsonSchemaMappingsConfigurationBase.SchemaInfo> list) throws ConfigurationException {
index a3c9d42d32d57809749120ee595225545cbd23ee..fcd53a9aba3df3ad8916132b9461cd1baf169c0b 100644 (file)
@@ -12,4 +12,7 @@ public interface JsonSchemaFileProvider {
 
   @Nullable
   Reader getSchemaReader();
+
+  @NotNull
+  String getName();
 }
index c68c556301e7b1c1b9cbd633e5ebdbe56a84bf81..b940856bb52473987eaaecdbb5fc2a30eb723c70 100644 (file)
@@ -43,16 +43,21 @@ public class JsonSchemaImportedProviderFactory implements JsonSchemaProviderFact
     final Map<String, JsonSchemaMappingsConfigurationBase.SchemaInfo> map = configuration.getStateMap();
     for (JsonSchemaMappingsConfigurationBase.SchemaInfo info : map.values()) {
       if (!info.getPatterns().isEmpty()) {
-        list.add(new MyProvider(project, configuration.convertToAbsoluteFile(info.getRelativePathToSchema()), info.getPatterns()));
+        list.add(new MyProvider(project, info.getName(), configuration.convertToAbsoluteFile(info.getRelativePathToSchema()), info.getPatterns()));
       }
     }
   }
 
   private static class MyProvider implements JsonSchemaFileProvider, JsonSchemaImportedProviderMarker {
+    @NotNull private final String myName;
     @NotNull private final File myFile;
     @NotNull private final List<Processor<VirtualFile>> myPatterns;
 
-    public MyProvider(@Nullable final Project project, @NotNull File file, @NotNull List<JsonSchemaMappingsConfigurationBase.Item> patterns) {
+    public MyProvider(@Nullable final Project project,
+                      @NotNull String name,
+                      @NotNull File file,
+                      @NotNull List<JsonSchemaMappingsConfigurationBase.Item> patterns) {
+      myName = name;
       myFile = file;
       myPatterns = new ArrayList<Processor<VirtualFile>>();
       for (final JsonSchemaMappingsConfigurationBase.Item pattern : patterns) {
@@ -95,6 +100,12 @@ public class JsonSchemaImportedProviderFactory implements JsonSchemaProviderFact
       }
     }
 
+    @NotNull
+    @Override
+    public String getName() {
+      return myName;
+    }
+
     @Override
     public boolean isAvailable(@NotNull VirtualFile file) {
       if (file.isDirectory() || !file.isValid()) return false;
index c33ca8030d0a62a3a49ef85919946edb33057748..0fa7c73def78ee740b14bd3e2305b9479ad6eccd 100644 (file)
@@ -52,6 +52,12 @@ public class JsonSchemaSelfProviderFactory implements JsonSchemaProviderFactory
           return content == null ? null : new StringReader(content);
         }
 
+        @NotNull
+        @Override
+        public String getName() {
+          return "schema.json";
+        }
+
         @Nullable
         private String getContent() {
           ClassLoader loader = JsonSchemaSelfProviderFactory.class.getClassLoader();
diff --git a/json/src/com/jetbrains/jsonSchema/impl/CodeInsightProviders.java b/json/src/com/jetbrains/jsonSchema/impl/CodeInsightProviders.java
new file mode 100644 (file)
index 0000000..3ba9f22
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.jsonSchema.impl;
+
+import com.intellij.codeInsight.completion.CompletionContributor;
+import com.intellij.lang.annotation.Annotator;
+import com.intellij.lang.documentation.DocumentationProvider;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Irina.Chernushina on 2/18/2016.
+ */
+public interface CodeInsightProviders {
+  @NotNull
+  CompletionContributor getContributor();
+
+  @NotNull
+  Annotator getAnnotator();
+
+  @NotNull
+  DocumentationProvider getDocumentationProvider();
+}
index 975962ed78963e6b6dd8d08d59ccb6e5a01ba0e5..2ccc2cd1c13dd6eb84616509d1a4c609dd89f164 100644 (file)
@@ -6,7 +6,9 @@ import com.intellij.lang.annotation.Annotator;
 import com.intellij.lang.documentation.DocumentationProvider;
 import org.jetbrains.annotations.NotNull;
 
-class JsonSchemaObjectCodeInsightWrapper {
+class JsonSchemaObjectCodeInsightWrapper implements CodeInsightProviders {
+  @NotNull private final String myName;
+  private boolean myIsUserSchema;
 
   @NotNull
   private final CompletionContributor myContributor;
@@ -16,24 +18,42 @@ class JsonSchemaObjectCodeInsightWrapper {
   @NotNull
   private final DocumentationProvider myDocumentationProvider;
 
-  public JsonSchemaObjectCodeInsightWrapper(@NotNull JsonSchemaObject schemaObject) {
+  public JsonSchemaObjectCodeInsightWrapper(@NotNull String name, @NotNull JsonSchemaObject schemaObject) {
+    myName = name;
     myContributor = new JsonBySchemaObjectCompletionContributor(schemaObject);
     myAnnotator = new JsonBySchemaObjectAnnotator(schemaObject);
     myDocumentationProvider = new JsonBySchemaDocumentationProvider(schemaObject);
   }
 
+  @Override
   @NotNull
   public CompletionContributor getContributor() {
     return myContributor;
   }
 
+  @Override
   @NotNull
   public Annotator getAnnotator() {
     return myAnnotator;
   }
 
+  @NotNull
+  public String getName() {
+    return myName;
+  }
+
+  @Override
   @NotNull
   public DocumentationProvider getDocumentationProvider() {
     return myDocumentationProvider;
   }
+
+  public JsonSchemaObjectCodeInsightWrapper setUserSchema(boolean userSchema) {
+    myIsUserSchema = userSchema;
+    return this;
+  }
+
+  public boolean isUserSchema() {
+    return myIsUserSchema;
+  }
 }
index 16d15962078cf0aaf08dd21de9ea986b615c6ebb..f0958257592ed5d6056a9d75498848df92f7e605 100644 (file)
@@ -2,21 +2,38 @@ package com.jetbrains.jsonSchema.impl;
 
 
 import com.intellij.codeInsight.completion.CompletionContributor;
+import com.intellij.codeInsight.completion.CompletionParameters;
+import com.intellij.codeInsight.completion.CompletionResultSet;
+import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
+import com.intellij.codeInsight.intention.IntentionAction;
 import com.intellij.idea.RareLogger;
+import com.intellij.lang.annotation.Annotation;
+import com.intellij.lang.annotation.AnnotationHolder;
 import com.intellij.lang.annotation.Annotator;
+import com.intellij.lang.documentation.CompositeDocumentationProvider;
 import com.intellij.lang.documentation.DocumentationProvider;
 import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.options.ShowSettingsUtil;
 import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.util.IncorrectOperationException;
 import com.intellij.util.containers.ContainerUtil;
+import com.jetbrains.jsonSchema.JsonSchemaMappingsConfigurable;
 import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider;
 import com.jetbrains.jsonSchema.extension.JsonSchemaImportedProviderMarker;
 import com.jetbrains.jsonSchema.extension.JsonSchemaProviderFactory;
 import com.jetbrains.jsonSchema.ide.JsonSchemaService;
+import org.jetbrains.annotations.Nls;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.ConcurrentMap;
 
 public class JsonSchemaServiceImpl implements JsonSchemaService {
@@ -38,25 +55,25 @@ public class JsonSchemaServiceImpl implements JsonSchemaService {
 
   @Nullable
   public Annotator getAnnotator(@Nullable VirtualFile file) {
-    JsonSchemaObjectCodeInsightWrapper wrapper = getWrapper(file);
+    CodeInsightProviders wrapper = getWrapper(file);
     return wrapper != null ? wrapper.getAnnotator() : null;
   }
 
   @Nullable
   public CompletionContributor getCompletionContributor(@Nullable VirtualFile file) {
-    JsonSchemaObjectCodeInsightWrapper wrapper = getWrapper(file);
+    CodeInsightProviders wrapper = getWrapper(file);
     return wrapper != null ? wrapper.getContributor() : null;
   }
 
   public boolean hasSchema(@Nullable VirtualFile file) {
-    JsonSchemaObjectCodeInsightWrapper wrapper = getWrapper(file);
+    CodeInsightProviders wrapper = getWrapper(file);
     return wrapper != null;
   }
 
   @Nullable
   @Override
   public DocumentationProvider getDocumentationProvider(@Nullable VirtualFile file) {
-    JsonSchemaObjectCodeInsightWrapper wrapper = getWrapper(file);
+    CodeInsightProviders wrapper = getWrapper(file);
     return wrapper != null ? wrapper.getDocumentationProvider() : null;
   }
 
@@ -66,7 +83,7 @@ public class JsonSchemaServiceImpl implements JsonSchemaService {
     try {
       if (reader != null) {
         JsonSchemaObject resultObject = new JsonSchemaReader().read(reader);
-        return new JsonSchemaObjectCodeInsightWrapper(resultObject);
+        return new JsonSchemaObjectCodeInsightWrapper(provider.getName(), resultObject).setUserSchema(provider instanceof JsonSchemaImportedProviderMarker);
       }
     }
     catch (Exception e) {
@@ -86,8 +103,9 @@ public class JsonSchemaServiceImpl implements JsonSchemaService {
   }
 
   @Nullable
-  private JsonSchemaObjectCodeInsightWrapper getWrapper(@Nullable VirtualFile file) {
+  private CodeInsightProviders getWrapper(@Nullable VirtualFile file) {
     if (file == null) return null;
+    final List<JsonSchemaObjectCodeInsightWrapper> wrappers = new ArrayList<>();
     JsonSchemaProviderFactory[] factories = getProviderFactories();
     for (JsonSchemaProviderFactory factory : factories) {
       for (JsonSchemaFileProvider provider : factory.getProviders(myProject)) {
@@ -99,13 +117,120 @@ public class JsonSchemaServiceImpl implements JsonSchemaService {
             myWrappers.putIfAbsent(provider, newWrapper);
             wrapper = myWrappers.get(provider);
           }
-
           if (wrapper != null) {
-            return wrapper;
+            wrappers.add(wrapper);
           }
         }
       }
     }
-    return null;
+
+    return wrappers.isEmpty() ? null : (wrappers.size() == 1 ? wrappers.get(0) : new CompositeCodeInsightProviderWithWarning(wrappers));
+  }
+
+  private static class CompositeCodeInsightProviderWithWarning implements CodeInsightProviders {
+    private final List<JsonSchemaObjectCodeInsightWrapper> myWrappers;
+    private CompletionContributor myContributor;
+    private Annotator myAnnotator;
+    private DocumentationProvider myDocumentationProvider;
+
+    public CompositeCodeInsightProviderWithWarning(List<JsonSchemaObjectCodeInsightWrapper> wrappers) {
+      myWrappers = wrappers;
+      myContributor = new CompletionContributor() {
+        @Override
+        public void fillCompletionVariants(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet result) {
+          for (JsonSchemaObjectCodeInsightWrapper wrapper : myWrappers) {
+            wrapper.getContributor().fillCompletionVariants(parameters, result);
+          }
+        }
+      };
+      myAnnotator = new Annotator() {
+        private String message;
+        {
+          boolean haveSystemSchemas = false;
+          for (JsonSchemaObjectCodeInsightWrapper wrapper : myWrappers) {
+            haveSystemSchemas |= !wrapper.isUserSchema();
+          }
+          boolean withTypes = haveSystemSchemas;
+          final List<String> names = new ArrayList<>();
+          for (JsonSchemaObjectCodeInsightWrapper wrapper : myWrappers) {
+            if (withTypes) {
+              names.add((wrapper.isUserSchema() ? "user" : "system") + " schema '" + wrapper.getName() + "'");
+            } else {
+              names.add(wrapper.getName());
+            }
+          }
+          message = "<html>There are several JSON Schemas mapped to this file: " + StringUtil.join(names, "; ") + "</html>";
+        }
+
+        @Override
+        public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {
+          if (element instanceof PsiFile) {
+            addFileLevelWarning(element, holder);
+          }
+          for (JsonSchemaObjectCodeInsightWrapper wrapper : myWrappers) {
+            wrapper.getAnnotator().annotate(element, holder);
+          }
+        }
+
+        private void addFileLevelWarning(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {
+          final Annotation annotation = holder.createErrorAnnotation(element, message);
+          annotation.setFileLevelAnnotation(true);
+          annotation.registerFix(new IntentionAction() {
+            @Nls
+            @NotNull
+            @Override
+            public String getText() {
+              return "Edit JSON Schema Mappings";
+            }
+
+            @Nls
+            @NotNull
+            @Override
+            public String getFamilyName() {
+              return "JSON Schema";
+            }
+
+            @Override
+            public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
+              return true;
+            }
+
+            @Override
+            public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
+              ShowSettingsUtil.getInstance().editConfigurable(project, new JsonSchemaMappingsConfigurable(project));
+              DaemonCodeAnalyzer.getInstance(project).restart(file);
+            }
+
+            @Override
+            public boolean startInWriteAction() {
+              return false;
+            }
+          });
+        }
+      };
+      final List<DocumentationProvider> list = new ArrayList<>();
+      for (JsonSchemaObjectCodeInsightWrapper wrapper : myWrappers) {
+        list.add(wrapper.getDocumentationProvider());
+      }
+      myDocumentationProvider = CompositeDocumentationProvider.wrapProviders(list);
+    }
+
+    @NotNull
+    @Override
+    public CompletionContributor getContributor() {
+      return myContributor;
+    }
+
+    @NotNull
+    @Override
+    public Annotator getAnnotator() {
+      return myAnnotator;
+    }
+
+    @NotNull
+    @Override
+    public DocumentationProvider getDocumentationProvider() {
+      return myDocumentationProvider;
+    }
   }
 }
index 204adff963262e43f9270f1e076b0536a539a255..17d0375530822cc58049808a2fae43dcea87d200 100644 (file)
@@ -28,4 +28,10 @@ public class JsonSchemaTestProvider implements JsonSchemaFileProvider {
   public Reader getSchemaReader() {
     return new StringReader(mySchemaText);
   }
+
+  @NotNull
+  @Override
+  public String getName() {
+    return "test";
+  }
 }