WEB-20367 JSON Schema: provide validation warning for the file under several schemas

Use all schemas available for the file; display warning message with quick fix to edit settings
This commit is contained in:
irengrig
2016-02-18 17:01:00 +01:00
parent 908f78ae6f
commit 1103799e62
8 changed files with 221 additions and 13 deletions
@@ -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 {
@@ -12,4 +12,7 @@ public interface JsonSchemaFileProvider {
@Nullable
Reader getSchemaReader();
@NotNull
String getName();
}
@@ -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;
@@ -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();
@@ -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();
}
@@ -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;
}
}
@@ -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;
}
}
}
@@ -28,4 +28,10 @@ public class JsonSchemaTestProvider implements JsonSchemaFileProvider {
public Reader getSchemaReader() {
return new StringReader(mySchemaText);
}
@NotNull
@Override
public String getName() {
return "test";
}
}