diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaAnnotatorChecker.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaAnnotatorChecker.java index 90fef6f27e4e..8986284f3958 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaAnnotatorChecker.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaAnnotatorChecker.java @@ -110,6 +110,7 @@ class JsonSchemaAnnotatorChecker { } else { final String typesText = Arrays.stream(allowedTypes) .map(JsonSchemaType::getName) + .distinct() .sorted(Comparator.naturalOrder()) .collect(Collectors.joining(", ")); error(String.format("Type is not allowed. Expected one of: %s.", typesText), value); @@ -122,29 +123,58 @@ class JsonSchemaAnnotatorChecker { public void checkByScheme(@NotNull JsonValueAdapter value, @NotNull JsonSchemaObject schema) { final JsonSchemaType type = JsonSchemaType.getType(value); + boolean matchingType = false; if (type != null) { JsonSchemaType schemaType = getMatchingSchemaType(schema, type); if (schemaType != null && !schemaType.equals(type)) { typeError(value.getDelegate(), schemaType); } - else if (JsonSchemaType._boolean.equals(type)) { - checkForEnum(value.getDelegate(), schema); + else { + matchingType = schema.getType() != null || schema.getTypeVariants() != null; + if (JsonSchemaType._boolean.equals(type)) { + checkForEnum(value.getDelegate(), schema); + } + else if (JsonSchemaType._number.equals(type) || JsonSchemaType._integer.equals(type)) { + checkNumber(value.getDelegate(), schema, type); + checkForEnum(value.getDelegate(), schema); + } + else if (JsonSchemaType._string.equals(type)) { + checkString(value.getDelegate(), schema); + checkForEnum(value.getDelegate(), schema); + } + else if (JsonSchemaType._array.equals(type)) { + checkArray(value, schema); + checkForEnum(value.getDelegate(), schema); + } + else if (JsonSchemaType._object.equals(type)) { + checkObject(value, schema); + checkForEnum(value.getDelegate(), schema); + } } - else if (JsonSchemaType._number.equals(type) || JsonSchemaType._integer.equals(type)) { - checkNumber(value.getDelegate(), schema, type); - checkForEnum(value.getDelegate(), schema); + } + + if ((!myHadTypeError || myErrors.isEmpty()) && !value.isShouldBeIgnored()) { + PsiElement delegate = value.getDelegate(); + checkForEnum(delegate, schema); + if (hasNumberChecks(schema) && value.isNumberLiteral()) { + checkNumber(delegate, schema, JsonSchemaType._number); } - else if (JsonSchemaType._string.equals(type)) { - checkString(value.getDelegate(), schema); - checkForEnum(value.getDelegate(), schema); + if (hasStringChecks(schema) && value.isStringLiteral()) { + checkString(delegate, schema); } - else if (JsonSchemaType._array.equals(type)) { + if (hasArrayChecks(schema) && value.isArray()) { checkArray(value, schema); - checkForEnum(value.getDelegate(), schema); } - else if (JsonSchemaType._object.equals(type)) { + if (hasMinMaxLengthChecks(schema)) { + if (value.isStringLiteral()) { + checkString(delegate, schema); + } + else if (value.isArray()) { + checkArray(value, schema); + } + } + if (hasObjectChecks(schema) && value.isObject()) { checkObject(value, schema); - checkForEnum(value.getDelegate(), schema); } } @@ -170,6 +200,16 @@ class JsonSchemaAnnotatorChecker { } } + private static boolean hasObjectChecks(JsonSchemaObject schema) { + return !schema.getProperties().isEmpty() + || schema.getPropertyNamesSchema() != null + || schema.getPropertyDependencies() != null + || schema.hasPatternProperties() + || schema.getRequired() != null + || schema.getMinProperties() != null + || schema.getMaxProperties() != null; + } + private void checkObject(@NotNull JsonValueAdapter value, @NotNull JsonSchemaObject schema) { final JsonObjectValueAdapter object = value.getAsObject(); if (object == null) return; @@ -357,6 +397,10 @@ class JsonSchemaAnnotatorChecker { .filter(schema -> areSchemaTypesCompatible(schema, type)) .collect(Collectors.toList()); if (filtered.isEmpty()) checker.typeError(value.getDelegate(), getExpectedTypes(collection)); + else if (filtered.size() == 1) { + selected = filtered.get(0); + checker.checkByScheme(value, selected); + } else { if (isOneOf) { selected = checker.processOneOf(value, filtered); @@ -421,6 +465,15 @@ class JsonSchemaAnnotatorChecker { return null; } + private static boolean hasArrayChecks(JsonSchemaObject schema) { + return schema.isUniqueItems() + || schema.getContainsSchema() != null + || schema.getItemsSchema() != null + || schema.getItemsSchemaList() != null + || schema.getMinItems() != null + || schema.getMaxItems() != null; + } + private void checkArrayItems(@NotNull JsonValueAdapter array, @NotNull final List list, final JsonSchemaObject schema) { if (schema.isUniqueItems()) { final MultiMap valueTexts = new MultiMap<>(); @@ -471,6 +524,14 @@ class JsonSchemaAnnotatorChecker { } } + private static boolean hasStringChecks(JsonSchemaObject schema) { + return schema.getPattern() != null || schema.getFormat() != null; + } + + private static boolean hasMinMaxLengthChecks(JsonSchemaObject schema) { + return schema.getMinLength() != null || schema.getMaxLength() != null; + } + private void checkString(PsiElement propValue, JsonSchemaObject schema) { final String value = StringUtil.unquoteString(propValue.getText()); if (schema.getMinLength() != null) { @@ -501,6 +562,14 @@ class JsonSchemaAnnotatorChecker { }*/ } + private static boolean hasNumberChecks(JsonSchemaObject schema) { + return schema.getMultipleOf() != null + || schema.getExclusiveMinimumNumber() != null + || schema.getExclusiveMaximumNumber() != null + || schema.getMaximum() != null + || schema.getMinimum() != null; + } + private void checkNumber(PsiElement propValue, JsonSchemaObject schema, JsonSchemaType schemaType) { Number value; if (JsonSchemaType._integer.equals(schemaType)) { diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java index 643b2252651c..6da6846d797b 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java @@ -9,7 +9,6 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -208,6 +207,10 @@ public class JsonSchemaObject { myProperties = properties; } + public boolean hasPatternProperties() { + return myPatternProperties != null; + } + public void setPatternProperties(@NotNull Map patternProperties) { myPatternProperties = new PatternProperties(patternProperties); } diff --git a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java index dc254502917a..7bb52b50511e 100644 --- a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java +++ b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java @@ -279,7 +279,7 @@ public class JsonSchemaHighlightingTest extends DaemonAnalyzerTestCase { public void testAllOfProperties() throws Exception { @Language("JSON") final String schema = "{\"allOf\": [{\"type\": \"object\", \"properties\": {\"first\": {}}}," + " {\"properties\": {\"second\": {\"enum\": [33,44]}}}], \"additionalProperties\": false}"; - doTest(schema, "{\"first\": {}, \"second\": null}"); + doTest(schema, "{\"first\": {}, \"second\": null}"); doTest(schema, "{\"first\": {}, \"second\": 44, \"other\": 15}"); doTest(schema, "{\"first\": {}, \"second\": 12}"); }