diff --git a/json/resources/inspectionDescriptions/JsonSchemaRefReference.html b/json/resources/inspectionDescriptions/JsonSchemaRefReference.html
new file mode 100644
index 000000000000..714ecb28b485
--- /dev/null
+++ b/json/resources/inspectionDescriptions/JsonSchemaRefReference.html
@@ -0,0 +1,5 @@
+
+
+This inspection checks that '$ref' and '$schema' paths are valid
+
+
\ No newline at end of file
diff --git a/json/src/com/intellij/json/JsonBundle.properties b/json/src/com/intellij/json/JsonBundle.properties
index 6726f8ef7cfa..a235cff493c8 100644
--- a/json/src/com/intellij/json/JsonBundle.properties
+++ b/json/src/com/intellij/json/JsonBundle.properties
@@ -25,7 +25,7 @@ inspection.compliance.option.trailing.comma=Warn about trailing commas
inspection.compliance.option.nan.infinity=Warn about NaN and Infinity/-Infinity numeric values
inspection.duplicate.keys.name=Duplicate keys in object literals
-inspection.duplicate.keys.msg.duplicate.keys=Object contains duplicate keys "{0}"
+inspection.duplicate.keys.msg.duplicate.keys=Object contains duplicate keys ''{0}''
# Formatter
formatter.align.properties.caption=Align
@@ -51,6 +51,12 @@ json.schema.file.not.found=File not found
json.schema.inspection.compliance.name=Compliance with JSON schema
json.schema.inspection.case.insensitive.enum=Case insensitive matching for enum values
+json.schema.ref.refs.inspection.name=Unresolved '$ref' and '$schema' references
+json.schema.ref.file.not.found=File ''{0}'' not found
+json.schema.ref.cannot.resolve.path=Cannot resolve path ''{0}''
+json.schema.ref.no.array.element=Array doesn''t contain element with index '{0}'
+json.schema.ref.no.property=Property ''{0}'' not found
+
settings.json.schema.add.mapping=Add mapping
settings.json.schema.edit.mapping=Edit mapping
settings.json.schema.remove.mapping=Remove mapping
\ No newline at end of file
diff --git a/json/src/com/jetbrains/jsonSchema/impl/inspections/JsonSchemaBasedInspectionBase.java b/json/src/com/jetbrains/jsonSchema/impl/inspections/JsonSchemaBasedInspectionBase.java
new file mode 100644
index 000000000000..0a47e699b722
--- /dev/null
+++ b/json/src/com/jetbrains/jsonSchema/impl/inspections/JsonSchemaBasedInspectionBase.java
@@ -0,0 +1,46 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.jetbrains.jsonSchema.impl.inspections;
+
+import com.intellij.codeHighlighting.HighlightDisplayLevel;
+import com.intellij.codeInspection.LocalInspectionTool;
+import com.intellij.codeInspection.LocalInspectionToolSession;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.json.psi.JsonFile;
+import com.intellij.json.psi.JsonValue;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiElementVisitor;
+import com.intellij.psi.PsiFile;
+import com.intellij.util.ObjectUtils;
+import com.jetbrains.jsonSchema.ide.JsonSchemaService;
+import com.jetbrains.jsonSchema.impl.JsonSchemaObject;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public abstract class JsonSchemaBasedInspectionBase extends LocalInspectionTool {
+ @NotNull
+ @Override
+ public HighlightDisplayLevel getDefaultLevel() {
+ return HighlightDisplayLevel.WARNING;
+ }
+
+ @NotNull
+ @Override
+ public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session) {
+ PsiFile file = holder.getFile();
+ JsonValue root = file instanceof JsonFile ? ObjectUtils.tryCast(file.getFirstChild(), JsonValue.class) : null;
+ if (root == null) return PsiElementVisitor.EMPTY_VISITOR;
+
+ JsonSchemaService service = JsonSchemaService.Impl.get(file.getProject());
+ VirtualFile virtualFile = file.getViewProvider().getVirtualFile();
+ if (!service.isApplicableToFile(virtualFile)) return PsiElementVisitor.EMPTY_VISITOR;
+ final JsonSchemaObject rootSchema = service.getSchemaObject(virtualFile);
+
+ return doBuildVisitor(root, rootSchema, service, holder, session);
+ }
+
+ protected abstract PsiElementVisitor doBuildVisitor(@NotNull JsonValue root,
+ @Nullable JsonSchemaObject schema,
+ @NotNull JsonSchemaService service,
+ @NotNull ProblemsHolder holder,
+ @NotNull LocalInspectionToolSession session);
+}
diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaComplianceInspection.java b/json/src/com/jetbrains/jsonSchema/impl/inspections/JsonSchemaComplianceInspection.java
similarity index 62%
rename from json/src/com/jetbrains/jsonSchema/impl/JsonSchemaComplianceInspection.java
rename to json/src/com/jetbrains/jsonSchema/impl/inspections/JsonSchemaComplianceInspection.java
index 492c5190480e..0ff947322fa8 100644
--- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaComplianceInspection.java
+++ b/json/src/com/jetbrains/jsonSchema/impl/inspections/JsonSchemaComplianceInspection.java
@@ -1,28 +1,25 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
-package com.jetbrains.jsonSchema.impl;
+package com.jetbrains.jsonSchema.impl.inspections;
-import com.intellij.codeHighlighting.HighlightDisplayLevel;
-import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.LocalInspectionToolSession;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
import com.intellij.json.JsonBundle;
import com.intellij.json.psi.JsonElementVisitor;
-import com.intellij.json.psi.JsonFile;
import com.intellij.json.psi.JsonValue;
-import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
-import com.intellij.psi.PsiFile;
-import com.intellij.util.ObjectUtils;
import com.jetbrains.jsonSchema.extension.JsonLikePsiWalker;
import com.jetbrains.jsonSchema.ide.JsonSchemaService;
+import com.jetbrains.jsonSchema.impl.JsonComplianceCheckerOptions;
+import com.jetbrains.jsonSchema.impl.JsonSchemaComplianceChecker;
+import com.jetbrains.jsonSchema.impl.JsonSchemaObject;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
-public class JsonSchemaComplianceInspection extends LocalInspectionTool {
+public class JsonSchemaComplianceInspection extends JsonSchemaBasedInspectionBase {
public boolean myCaseInsensitiveEnum = false;
@Override
@@ -31,24 +28,11 @@ public class JsonSchemaComplianceInspection extends LocalInspectionTool {
return JsonBundle.message("json.schema.inspection.compliance.name");
}
- @NotNull
@Override
- public HighlightDisplayLevel getDefaultLevel() {
- return HighlightDisplayLevel.WARNING;
- }
-
- @NotNull
- @Override
- public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session) {
- PsiFile file = holder.getFile();
- JsonValue root = file instanceof JsonFile ? ObjectUtils.tryCast(file.getFirstChild(), JsonValue.class) : null;
- if (root == null) return PsiElementVisitor.EMPTY_VISITOR;
-
- JsonSchemaService service = JsonSchemaService.Impl.get(file.getProject());
- VirtualFile virtualFile = file.getViewProvider().getVirtualFile();
- if (!service.isApplicableToFile(virtualFile)) return PsiElementVisitor.EMPTY_VISITOR;
- final JsonSchemaObject rootSchema = service.getSchemaObject(virtualFile);
- if (rootSchema == null) return PsiElementVisitor.EMPTY_VISITOR;
+ protected PsiElementVisitor doBuildVisitor(@NotNull JsonValue root, @Nullable JsonSchemaObject schema, @NotNull JsonSchemaService service,
+ @NotNull ProblemsHolder holder,
+ @NotNull LocalInspectionToolSession session) {
+ if (schema == null) return PsiElementVisitor.EMPTY_VISITOR;
JsonComplianceCheckerOptions options = new JsonComplianceCheckerOptions(myCaseInsensitiveEnum);
return new JsonElementVisitor() {
@@ -56,7 +40,7 @@ public class JsonSchemaComplianceInspection extends LocalInspectionTool {
public void visitElement(PsiElement element) {
if (element == root) {
// perform this only for the root element, because the checker traverses the hierarchy itself
- annotate(element, rootSchema, holder, session, options);
+ annotate(element, schema, holder, session, options);
}
super.visitElement(element);
}
diff --git a/json/src/com/jetbrains/jsonSchema/impl/inspections/JsonSchemaRefReferenceInspection.java b/json/src/com/jetbrains/jsonSchema/impl/inspections/JsonSchemaRefReferenceInspection.java
new file mode 100644
index 000000000000..59cd920fee91
--- /dev/null
+++ b/json/src/com/jetbrains/jsonSchema/impl/inspections/JsonSchemaRefReferenceInspection.java
@@ -0,0 +1,89 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.jetbrains.jsonSchema.impl.inspections;
+
+import com.intellij.codeInspection.LocalInspectionToolSession;
+import com.intellij.codeInspection.ProblemHighlightType;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.json.JsonBundle;
+import com.intellij.json.psi.*;
+import com.intellij.openapi.paths.WebReference;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementVisitor;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReference;
+import com.jetbrains.jsonSchema.ide.JsonSchemaService;
+import com.jetbrains.jsonSchema.impl.JsonSchemaObject;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class JsonSchemaRefReferenceInspection extends JsonSchemaBasedInspectionBase {
+ @Override
+ @NotNull
+ public String getDisplayName() {
+ return JsonBundle.message("json.schema.ref.refs.inspection.name");
+ }
+
+ @Override
+ protected PsiElementVisitor doBuildVisitor(@NotNull JsonValue root,
+ @Nullable JsonSchemaObject schema,
+ @NotNull JsonSchemaService service,
+ @NotNull ProblemsHolder holder,
+ @NotNull LocalInspectionToolSession session) {
+ boolean checkRefs = schema != null && service.isSchemaFile(schema.getSchemaFile());
+ return new JsonElementVisitor() {
+ @Override
+ public void visitElement(PsiElement element) {
+ if (element == root) {
+ if (element instanceof JsonObject) {
+ final JsonProperty schemaProp = ((JsonObject)element).findProperty("$schema");
+ if (schemaProp != null) {
+ doCheck(schemaProp.getValue());
+ }
+ }
+ }
+ super.visitElement(element);
+ }
+
+ @Override
+ public void visitProperty(@NotNull JsonProperty o) {
+ if (!checkRefs) return;
+ if ("$ref".equals(o.getName())) {
+ doCheck(o.getValue());
+ }
+ super.visitProperty(o);
+ }
+
+ private void doCheck(JsonValue value) {
+ if (!(value instanceof JsonStringLiteral)) return;
+ for (PsiReference reference : value.getReferences()) {
+ if (reference instanceof WebReference) continue;
+ final PsiElement resolved = reference.resolve();
+ if (resolved == null) {
+ holder.registerProblem(reference, getReferenceErrorDesc(reference), ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
+ }
+ }
+ }
+
+ private String getReferenceErrorDesc(PsiReference reference) {
+ final String text = reference.getCanonicalText();
+ if (reference instanceof FileReference) {
+ final int hash = text.indexOf('#');
+ return JsonBundle.message("json.schema.ref.file.not.found", hash == -1 ? text : text.substring(0, hash));
+ }
+ final int lastSlash = text.lastIndexOf('/');
+ if (lastSlash == -1) {
+ return JsonBundle.message("json.schema.ref.cannot.resolve.path", text);
+ }
+ final String substring = text.substring(text.lastIndexOf('/') + 1);
+
+ try {
+ Integer.parseInt(substring);
+ return JsonBundle.message("json.schema.ref.no.array.element", substring);
+ }
+ catch (Exception e) {
+ return JsonBundle.message("json.schema.ref.no.property", substring);
+ }
+ }
+ };
+ }
+}
diff --git a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaCrossReferencesTest.java b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaCrossReferencesTest.java
index ff6083cb7c82..ec3e0bd23a8b 100644
--- a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaCrossReferencesTest.java
+++ b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaCrossReferencesTest.java
@@ -25,9 +25,9 @@ import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider;
import com.jetbrains.jsonSchema.extension.JsonSchemaProjectSelfProviderFactory;
import com.jetbrains.jsonSchema.ide.JsonSchemaService;
-import com.jetbrains.jsonSchema.impl.JsonSchemaComplianceInspection;
import com.jetbrains.jsonSchema.impl.JsonSchemaObject;
import com.jetbrains.jsonSchema.impl.JsonSchemaVersion;
+import com.jetbrains.jsonSchema.impl.inspections.JsonSchemaComplianceInspection;
import com.jetbrains.jsonSchema.schemaFile.TestJsonSchemaMappingsProjectConfiguration;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
diff --git a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java
index d5038ded0218..06317d9fc080 100644
--- a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java
+++ b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java
@@ -9,7 +9,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.util.containers.Predicate;
-import com.jetbrains.jsonSchema.impl.JsonSchemaComplianceInspection;
+import com.jetbrains.jsonSchema.impl.inspections.JsonSchemaComplianceInspection;
import org.intellij.lang.annotations.Language;
import java.io.File;
diff --git a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaReSharperHighlightingTest.java b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaReSharperHighlightingTest.java
index a61618e13573..b901387286b2 100644
--- a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaReSharperHighlightingTest.java
+++ b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaReSharperHighlightingTest.java
@@ -10,7 +10,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.ExpectedHighlightingData;
import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.util.containers.Predicate;
-import com.jetbrains.jsonSchema.impl.JsonSchemaComplianceInspection;
+import com.jetbrains.jsonSchema.impl.inspections.JsonSchemaComplianceInspection;
import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.NotNull;
diff --git a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaSelfHighligthingTest.java b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaSelfHighligthingTest.java
index 1cf5375ca58a..29a5cabc32aa 100644
--- a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaSelfHighligthingTest.java
+++ b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaSelfHighligthingTest.java
@@ -17,8 +17,8 @@ package com.jetbrains.jsonSchema;
import com.intellij.openapi.editor.impl.DocumentImpl;
import com.intellij.testFramework.ExpectedHighlightingData;
-import com.jetbrains.jsonSchema.impl.JsonSchemaComplianceInspection;
import com.jetbrains.jsonSchema.impl.JsonSchemaVersion;
+import com.jetbrains.jsonSchema.impl.inspections.JsonSchemaComplianceInspection;
import java.util.Collections;
diff --git a/json/tests/test/com/jetbrains/jsonSchema/fixes/JsonSchemaQuickFixTest.java b/json/tests/test/com/jetbrains/jsonSchema/fixes/JsonSchemaQuickFixTest.java
index 44e88f4eb6cb..4274441e3fc2 100644
--- a/json/tests/test/com/jetbrains/jsonSchema/fixes/JsonSchemaQuickFixTest.java
+++ b/json/tests/test/com/jetbrains/jsonSchema/fixes/JsonSchemaQuickFixTest.java
@@ -6,7 +6,7 @@ import com.intellij.json.JsonLanguage;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.Predicate;
-import com.jetbrains.jsonSchema.impl.JsonSchemaComplianceInspection;
+import com.jetbrains.jsonSchema.impl.inspections.JsonSchemaComplianceInspection;
public class JsonSchemaQuickFixTest extends JsonSchemaQuickFixTestBase {
@Override
diff --git a/json/tests/test/com/jetbrains/jsonSchema/impl/JsonSchemaReadTest.java b/json/tests/test/com/jetbrains/jsonSchema/impl/JsonSchemaReadTest.java
index cb44733f4f5e..5600c3d07d63 100644
--- a/json/tests/test/com/jetbrains/jsonSchema/impl/JsonSchemaReadTest.java
+++ b/json/tests/test/com/jetbrains/jsonSchema/impl/JsonSchemaReadTest.java
@@ -15,6 +15,7 @@ import com.jetbrains.jsonSchema.JsonSchemaTestServiceImpl;
import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider;
import com.jetbrains.jsonSchema.extension.JsonSchemaProjectSelfProviderFactory;
import com.jetbrains.jsonSchema.ide.JsonSchemaService;
+import com.jetbrains.jsonSchema.impl.inspections.JsonSchemaComplianceInspection;
import org.junit.Assert;
import java.io.File;
diff --git a/json/tests/testData/highlighting/DuplicatePropertyKeys.json b/json/tests/testData/highlighting/DuplicatePropertyKeys.json
index 485d44ecca61..d04ac4115814 100644
--- a/json/tests/testData/highlighting/DuplicatePropertyKeys.json
+++ b/json/tests/testData/highlighting/DuplicatePropertyKeys.json
@@ -1,9 +1,9 @@
{
"foo": {
- "foo": 1,
- 'foo': 2,
- foo: 3,
- "\u0066\u006f\u006f": {
+ "foo": 1,
+ 'foo': 2,
+ foo: 3,
+ "\u0066\u006f\u006f": {
"foo": 4
}
}
diff --git a/platform/platform-resources/src/META-INF/JsonPlugin.xml b/platform/platform-resources/src/META-INF/JsonPlugin.xml
index 9575d30dea16..5ef48e1d3482 100644
--- a/platform/platform-resources/src/META-INF/JsonPlugin.xml
+++ b/platform/platform-resources/src/META-INF/JsonPlugin.xml
@@ -79,7 +79,11 @@
+ implementationClass="com.jetbrains.jsonSchema.impl.inspections.JsonSchemaComplianceInspection"/>
+