diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaAnnotatorChecker.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaAnnotatorChecker.java index 2f1d71e63155..1da1e687bd9a 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaAnnotatorChecker.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaAnnotatorChecker.java @@ -21,6 +21,7 @@ import com.jetbrains.jsonSchema.extension.adapters.JsonObjectValueAdapter; import com.jetbrains.jsonSchema.extension.adapters.JsonPropertyAdapter; import com.jetbrains.jsonSchema.extension.adapters.JsonValueAdapter; import com.jetbrains.jsonSchema.ide.JsonSchemaService; +import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -101,17 +102,21 @@ class JsonSchemaAnnotatorChecker { return checker; } - private void error(final String error, final PsiElement holder) { - error(error, holder, JsonValidationError.FixableIssueKind.None, null); + private void error(final String error, final PsiElement holder, + @MagicConstant(flagsFromClass = JsonValidationError.class) int priority) { + error(error, holder, JsonValidationError.FixableIssueKind.None, null, priority); } private void error(final PsiElement newHolder, JsonValidationError error) { - error(error.getMessage(), newHolder, error.getFixableIssueKind(), error.getIssueData()); + error(error.getMessage(), newHolder, error.getFixableIssueKind(), error.getIssueData(), error.getPriority()); } - private void error(final String error, final PsiElement holder, JsonValidationError.FixableIssueKind fixableIssueKind, JsonValidationError.IssueData data) { + private void error(final String error, final PsiElement holder, + JsonValidationError.FixableIssueKind fixableIssueKind, + JsonValidationError.IssueData data, + @MagicConstant(flagsFromClass = JsonValidationError.class) int priority) { if (myErrors.containsKey(holder)) return; - myErrors.put(holder, new JsonValidationError(error, fixableIssueKind, data)); + myErrors.put(holder, new JsonValidationError(error, fixableIssueKind, data, priority)); } private void typeError(final @NotNull PsiElement value, final @NotNull JsonSchemaType... allowedTypes) { @@ -119,7 +124,8 @@ class JsonSchemaAnnotatorChecker { if (allowedTypes.length == 1) { error(String.format("Type is not allowed. Expected: %s.", allowedTypes[0].getName()), value, JsonValidationError.FixableIssueKind.ProhibitedType, - new JsonValidationError.TypeMismatchIssueData(allowedTypes)); + new JsonValidationError.TypeMismatchIssueData(allowedTypes), + JsonValidationError.HIGH_PRIORITY); } else { final String typesText = Arrays.stream(allowedTypes) .map(JsonSchemaType::getName) @@ -128,10 +134,11 @@ class JsonSchemaAnnotatorChecker { .collect(Collectors.joining(", ")); error(String.format("Type is not allowed. Expected one of: %s.", typesText), value, JsonValidationError.FixableIssueKind.ProhibitedType, - new JsonValidationError.TypeMismatchIssueData(allowedTypes)); + new JsonValidationError.TypeMismatchIssueData(allowedTypes), + JsonValidationError.HIGH_PRIORITY); } } else { - error("Type is not allowed", value); + error("Type is not allowed", value, JsonValidationError.HIGH_PRIORITY); } myHadTypeError = true; } @@ -206,7 +213,7 @@ class JsonSchemaAnnotatorChecker { .anyMatch(s -> schema.getJsonObject().equals(s.getJsonObject()))) return; final JsonSchemaAnnotatorChecker checker = checkByMatchResult(value, result); - if (checker == null || checker.isCorrect()) error("Validates against 'not' schema", value.getDelegate()); + if (checker == null || checker.isCorrect()) error("Validates against 'not' schema", value.getDelegate(), JsonValidationError.MEDIUM_PRIORITY); } if (schema.getIf() != null) { @@ -218,7 +225,7 @@ class JsonSchemaAnnotatorChecker { if (checker.isCorrect()) { JsonSchemaObject then = schema.getThen(); if (then == null) { - error("Validates against 'if' branch but no 'then' branch is present", value.getDelegate()); + error("Validates against 'if' branch but no 'then' branch is present", value.getDelegate(), JsonValidationError.LOW_PRIORITY); } else { checkObjectBySchemaRecordErrors(then, value); @@ -227,7 +234,7 @@ class JsonSchemaAnnotatorChecker { else { JsonSchemaObject schemaElse = schema.getElse(); if (schemaElse == null) { - error("Validates counter 'if' branch but no 'else' branch is present", value.getDelegate()); + error("Validates counter 'if' branch but no 'else' branch is present", value.getDelegate(), JsonValidationError.LOW_PRIORITY); } else { checkObjectBySchemaRecordErrors(schemaElse, value); @@ -277,7 +284,7 @@ class JsonSchemaAnnotatorChecker { if (ThreeState.NO.equals(pair.getFirst()) && !set.contains(name)) { error(JsonBundle.message("json.schema.annotation.not.allowed.property", name), property.getDelegate(), JsonValidationError.FixableIssueKind.ProhibitedProperty, - new JsonValidationError.ProhibitedPropertyIssueData(name)); + new JsonValidationError.ProhibitedPropertyIssueData(name), JsonValidationError.LOW_PRIORITY); } else if (ThreeState.UNSURE.equals(pair.getFirst()) && property.getValue() != null) { checkObjectBySchemaRecordErrors(pair.getSecond(), property.getValue()); @@ -292,14 +299,15 @@ class JsonSchemaAnnotatorChecker { requiredNames.removeAll(set); if (!requiredNames.isEmpty()) { JsonValidationError.MissingMultiplePropsIssueData data = createMissingPropertiesData(schema, requiredNames); - error("Missing required " + data.getMessage(false), value.getDelegate(), JsonValidationError.FixableIssueKind.MissingProperty, data); + error("Missing required " + data.getMessage(false), value.getDelegate(), JsonValidationError.FixableIssueKind.MissingProperty, data, + JsonValidationError.HIGH_PRIORITY); } } if (schema.getMinProperties() != null && propertyList.size() < schema.getMinProperties()) { - error("Number of properties is less than " + schema.getMinProperties(), value.getDelegate()); + error("Number of properties is less than " + schema.getMinProperties(), value.getDelegate(), JsonValidationError.LOW_PRIORITY); } if (schema.getMaxProperties() != null && propertyList.size() > schema.getMaxProperties()) { - error("Number of properties is greater than " + schema.getMaxProperties(), value.getDelegate()); + error("Number of properties is greater than " + schema.getMaxProperties(), value.getDelegate(), JsonValidationError.LOW_PRIORITY); } final Map> dependencies = schema.getPropertyDependencies(); if (dependencies != null) { @@ -313,7 +321,7 @@ class JsonSchemaAnnotatorChecker { error("Dependency is violated: " + data.getMessage(false) + " must be specified, since '" + entry.getKey() + "' is specified", value.getDelegate(), JsonValidationError.FixableIssueKind.MissingProperty, - data); + data, JsonValidationError.HIGH_PRIORITY); } } } @@ -409,7 +417,7 @@ class JsonSchemaAnnotatorChecker { final JsonProperty pattern = ((JsonObject)element).findProperty("pattern"); if (pattern != null) { - error(StringUtil.convertLineSeparators(patternError), pattern.getValue()); + error(StringUtil.convertLineSeparators(patternError), pattern.getValue(), JsonValidationError.LOW_PRIORITY); } } } @@ -423,7 +431,7 @@ class JsonSchemaAnnotatorChecker { if (element == null || !element.isValid()) continue; final PsiElement parent = element.getParent(); if (parent instanceof JsonProperty) { - error(StringUtil.convertLineSeparators(entry.getValue()), ((JsonProperty)parent).getNameElement()); + error(StringUtil.convertLineSeparators(entry.getValue()), ((JsonProperty)parent).getNameElement(), JsonValidationError.LOW_PRIORITY); } } } @@ -460,7 +468,7 @@ class JsonSchemaAnnotatorChecker { } } error("Value should be one of: [" + StringUtil.join(objects, o -> o.toString(), ", ") + "]", value, - JsonValidationError.FixableIssueKind.NonEnumValue, null); + JsonValidationError.FixableIssueKind.NonEnumValue, null, JsonValidationError.MEDIUM_PRIORITY); } private static boolean equalsIgnoreQuotesAndCase(@NotNull final String s1, @NotNull final String s2, boolean requireQuotedValues) { @@ -476,7 +484,7 @@ class JsonSchemaAnnotatorChecker { if (asArray == null) return; final List elements = asArray.getElements(); if (schema.getMinLength() != null && elements.size() < schema.getMinLength()) { - error("Array is shorter than " + schema.getMinLength(), value.getDelegate()); + error("Array is shorter than " + schema.getMinLength(), value.getDelegate(), JsonValidationError.LOW_PRIORITY); return; } checkArrayItems(value, elements, schema); @@ -594,7 +602,7 @@ class JsonSchemaAnnotatorChecker { for (Map.Entry> entry: valueTexts.entrySet()) { if (entry.getValue().size() > 1) { for (JsonValueAdapter item: entry.getValue()) { - error("Item is not unique", item.getDelegate()); + error("Item is not unique", item.getDelegate(), JsonValidationError.HIGH_PRIORITY); } } } @@ -609,7 +617,7 @@ class JsonSchemaAnnotatorChecker { } } if (!match) { - error("No match for 'contains' rule", array.getDelegate()); + error("No match for 'contains' rule", array.getDelegate(), JsonValidationError.MEDIUM_PRIORITY); } } if (schema.getItemsSchema() != null) { @@ -625,7 +633,7 @@ class JsonSchemaAnnotatorChecker { } else { if (!Boolean.TRUE.equals(schema.getAdditionalItemsAllowed())) { - error("Additional items are not allowed", arrayValue.getDelegate()); + error("Additional items are not allowed", arrayValue.getDelegate(), JsonValidationError.LOW_PRIORITY); } else if (schema.getAdditionalItemsSchema() != null) { checkObjectBySchemaRecordErrors(schema.getAdditionalItemsSchema(), arrayValue); @@ -634,10 +642,10 @@ class JsonSchemaAnnotatorChecker { } } if (schema.getMinItems() != null && list.size() < schema.getMinItems()) { - error("Array is shorter than " + schema.getMinItems(), array.getDelegate()); + error("Array is shorter than " + schema.getMinItems(), array.getDelegate(), JsonValidationError.LOW_PRIORITY); } if (schema.getMaxItems() != null && list.size() > schema.getMaxItems()) { - error("Array is longer than " + schema.getMaxItems(), array.getDelegate()); + error("Array is longer than " + schema.getMaxItems(), array.getDelegate(), JsonValidationError.LOW_PRIORITY); } } @@ -655,23 +663,23 @@ class JsonSchemaAnnotatorChecker { final String value = StringUtil.unquoteString(walker.getNodeTextForValidation(propValue)); if (schema.getMinLength() != null) { if (value.length() < schema.getMinLength()) { - error("String is shorter than " + schema.getMinLength(), propValue); + error("String is shorter than " + schema.getMinLength(), propValue, JsonValidationError.LOW_PRIORITY); return; } } if (schema.getMaxLength() != null) { if (value.length() > schema.getMaxLength()) { - error("String is longer than " + schema.getMaxLength(), propValue); + error("String is longer than " + schema.getMaxLength(), propValue, JsonValidationError.LOW_PRIORITY); return; } } if (schema.getPattern() != null) { if (schema.getPatternError() != null) { error("Can not check string by pattern because of error: " + StringUtil.convertLineSeparators(schema.getPatternError()), - propValue); + propValue, JsonValidationError.LOW_PRIORITY); } if (!schema.checkByPattern(value)) { - error("String is violating the pattern: '" + StringUtil.convertLineSeparators(schema.getPattern()) + "'", propValue); + error("String is violating the pattern: '" + StringUtil.convertLineSeparators(schema.getPattern()) + "'", propValue, JsonValidationError.LOW_PRIORITY); } } // I think we are not gonna to support format, there are a couple of RFCs there to check upon.. @@ -701,7 +709,7 @@ class JsonSchemaAnnotatorChecker { catch (NumberFormatException e) { error("Integer value expected", propValue, JsonValidationError.FixableIssueKind.TypeMismatch, - new JsonValidationError.TypeMismatchIssueData(new JsonSchemaType[]{schemaType})); + new JsonValidationError.TypeMismatchIssueData(new JsonSchemaType[]{schemaType}), JsonValidationError.HIGH_PRIORITY); return; } } @@ -713,7 +721,7 @@ class JsonSchemaAnnotatorChecker { if (!JsonSchemaType._string_number.equals(schemaType)) { error("Double value expected", propValue, JsonValidationError.FixableIssueKind.TypeMismatch, - new JsonValidationError.TypeMismatchIssueData(new JsonSchemaType[]{schemaType})); + new JsonValidationError.TypeMismatchIssueData(new JsonSchemaType[]{schemaType}), JsonValidationError.HIGH_PRIORITY); } return; } @@ -724,7 +732,7 @@ class JsonSchemaAnnotatorChecker { if (leftOver > 0.000001) { final String multipleOfValue = String.valueOf(Math.abs(multipleOf.doubleValue() - multipleOf.intValue()) < 0.000001 ? multipleOf.intValue() : multipleOf); - error("Is not multiple of " + multipleOfValue, propValue); + error("Is not multiple of " + multipleOfValue, propValue, JsonValidationError.LOW_PRIORITY); return; } } @@ -741,13 +749,13 @@ class JsonSchemaAnnotatorChecker { if (JsonSchemaType._integer.equals(propValueType)) { final int intValue = exclusiveMaximumNumber.intValue(); if (value.intValue() >= intValue) { - error("Greater than an exclusive maximum " + intValue, propertyValue); + error("Greater than an exclusive maximum " + intValue, propertyValue, JsonValidationError.LOW_PRIORITY); } } else { final double doubleValue = exclusiveMaximumNumber.doubleValue(); if (value.doubleValue() >= doubleValue) { - error("Greater than an exclusive maximum " + exclusiveMaximumNumber, propertyValue); + error("Greater than an exclusive maximum " + exclusiveMaximumNumber, propertyValue, JsonValidationError.LOW_PRIORITY); } } } @@ -758,12 +766,12 @@ class JsonSchemaAnnotatorChecker { final int intValue = maximum.intValue(); if (isExclusive) { if (value.intValue() >= intValue) { - error("Greater than an exclusive maximum " + intValue, propertyValue); + error("Greater than an exclusive maximum " + intValue, propertyValue, JsonValidationError.LOW_PRIORITY); } } else { if (value.intValue() > intValue) { - error("Greater than a maximum " + intValue, propertyValue); + error("Greater than a maximum " + intValue, propertyValue, JsonValidationError.LOW_PRIORITY); } } } @@ -771,12 +779,12 @@ class JsonSchemaAnnotatorChecker { final double doubleValue = maximum.doubleValue(); if (isExclusive) { if (value.doubleValue() >= doubleValue) { - error("Greater than an exclusive maximum " + maximum, propertyValue); + error("Greater than an exclusive maximum " + maximum, propertyValue, JsonValidationError.LOW_PRIORITY); } } else { if (value.doubleValue() > doubleValue) { - error("Greater than a maximum " + maximum, propertyValue); + error("Greater than a maximum " + maximum, propertyValue, JsonValidationError.LOW_PRIORITY); } } } @@ -790,13 +798,13 @@ class JsonSchemaAnnotatorChecker { if (JsonSchemaType._integer.equals(schemaType)) { final int intValue = exclusiveMinimumNumber.intValue(); if (value.intValue() <= intValue) { - error("Less than an exclusive minimum" + intValue, propertyValue); + error("Less than an exclusive minimum" + intValue, propertyValue, JsonValidationError.LOW_PRIORITY); } } else { final double doubleValue = exclusiveMinimumNumber.doubleValue(); if (value.doubleValue() <= doubleValue) { - error("Less than an exclusive minimum " + exclusiveMinimumNumber, propertyValue); + error("Less than an exclusive minimum " + exclusiveMinimumNumber, propertyValue, JsonValidationError.LOW_PRIORITY); } } } @@ -808,12 +816,12 @@ class JsonSchemaAnnotatorChecker { final int intValue = minimum.intValue(); if (isExclusive) { if (value.intValue() <= intValue) { - error("Less than an exclusive minimum " + intValue, propertyValue); + error("Less than an exclusive minimum " + intValue, propertyValue, JsonValidationError.LOW_PRIORITY); } } else { if (value.intValue() < intValue) { - error("Less than a minimum " + intValue, propertyValue); + error("Less than a minimum " + intValue, propertyValue, JsonValidationError.LOW_PRIORITY); } } } @@ -821,12 +829,12 @@ class JsonSchemaAnnotatorChecker { final double doubleValue = minimum.doubleValue(); if (isExclusive) { if (value.doubleValue() <= doubleValue) { - error("Less than an exclusive minimum " + minimum, propertyValue); + error("Less than an exclusive minimum " + minimum, propertyValue, JsonValidationError.LOW_PRIORITY); } } else { if (value.doubleValue() < doubleValue) { - error("Less than a minimum " + minimum, propertyValue); + error("Less than a minimum " + minimum, propertyValue, JsonValidationError.LOW_PRIORITY); } } } @@ -834,10 +842,11 @@ class JsonSchemaAnnotatorChecker { // returns the schema, selected for annotation private JsonSchemaObject processOneOf(@NotNull JsonValueAdapter value, List oneOf) { - final Map errors = new HashMap<>(); + final List candidateErroneousCheckers = ContainerUtil.newArrayList(); + final List candidateErroneousSchemas = ContainerUtil.newArrayList(); boolean wasTypeError = false; final List correct = new SmartList<>(); - JsonSchemaObject current = null; + int errorCount = 0; for (JsonSchemaObject object : oneOf) { // skip it if something JS awaited, we do not process it currently if (object.isShouldValidateAgainstJSType()) continue; @@ -846,16 +855,17 @@ class JsonSchemaAnnotatorChecker { checker.checkByScheme(value, object); if (checker.isCorrect()) { - current = object; - errors.clear(); + candidateErroneousCheckers.clear(); + candidateErroneousSchemas.clear(); correct.add(object); + errorCount = 0; } else { - if (errors.isEmpty() || wasTypeError && !checker.isHadTypeError() || errors.size() > checker.getErrors().size()) { + if (errorCount == 0 || wasTypeError && !checker.isHadTypeError() || errorCount > checker.getErrors().size()) { + candidateErroneousCheckers.add(checker); + candidateErroneousSchemas.add(object); + errorCount = checker.getErrors().size(); wasTypeError = checker.isHadTypeError(); - current = object; - errors.clear(); - errors.putAll(checker.getErrors()); } } } @@ -865,28 +875,51 @@ class JsonSchemaAnnotatorChecker { if (type != null) { // also check maybe some currently not checked properties like format are different with schemes if (!schemesDifferWithNotCheckedProperties(correct)) { - error("Validates to more than one variant", value.getDelegate()); + error("Validates to more than one variant", value.getDelegate(), JsonValidationError.MEDIUM_PRIORITY); } } + return ContainerUtil.getLastItem(correct); } - else { - if (!errors.isEmpty()) { - for (Map.Entry entry : errors.entrySet()) { - error(entry.getKey(), entry.getValue()); - } - } - } - return current; + + return showErrorsAndGetLeastErroneous(candidateErroneousCheckers, candidateErroneousSchemas); } private static boolean schemesDifferWithNotCheckedProperties(@NotNull final List list) { return list.stream().anyMatch(s -> !StringUtil.isEmptyOrSpaces(s.getFormat())); } + private enum Average { + Light, + Medium, + Hard + } + + @NotNull + private static Average getAverage(@NotNull JsonSchemaAnnotatorChecker checker) { + int lowPriorityCount = 0; + boolean hasMedium = false; + Collection values = checker.getErrors().values(); + for (JsonValidationError value: values) { + switch (value.getPriority()) { + case JsonValidationError.LOW_PRIORITY: + lowPriorityCount++; + break; + case JsonValidationError.MEDIUM_PRIORITY: + hasMedium = true; + break; + case JsonValidationError.HIGH_PRIORITY: + return Average.Hard; + } + } + + return lowPriorityCount <= 3 && !hasMedium ? Average.Light : Average.Medium; + } + // returns the schema, selected for annotation private JsonSchemaObject processAnyOf(@NotNull JsonValueAdapter value, List anyOf) { - final Map errors = new HashMap<>(); - JsonSchemaObject current = null; + final List candidateErroneousCheckers = ContainerUtil.newArrayList(); + final List candidateErroneousSchemas = ContainerUtil.newArrayList(); + for (JsonSchemaObject object : anyOf) { final JsonSchemaAnnotatorChecker checker = new JsonSchemaAnnotatorChecker(); checker.checkByScheme(value, object); @@ -894,17 +927,44 @@ class JsonSchemaAnnotatorChecker { return object; } // maybe we still find the correct schema - continue to iterate - if (errors.isEmpty() && !checker.isHadTypeError()) { - current = object; - errors.clear(); - errors.putAll(checker.getErrors()); + if (!checker.isHadTypeError()) { + candidateErroneousCheckers.add(checker); + candidateErroneousSchemas.add(object); } } - if (!errors.isEmpty()) { - for (Map.Entry entry : errors.entrySet()) { - error(entry.getKey(), entry.getValue()); + + return showErrorsAndGetLeastErroneous(candidateErroneousCheckers, candidateErroneousSchemas); + } + + /** + * Filters schema validation results to get the result with the "minimal" amount of errors. + * This is needed in case of oneOf or anyOf conditions, when there exist no match. + * I.e., when we have multiple schema candidates, but none is applicable. + * In this case we need to show the most "suitable" error messages + * - by detecting the most "likely" schema corresponding to the current entity + */ + @NotNull + private JsonSchemaObject showErrorsAndGetLeastErroneous(@NotNull List candidateErroneousCheckers, + @NotNull List candidateErroneousSchemas) { + JsonSchemaObject current = null; + Optional minAverage = candidateErroneousCheckers.stream().map(c -> getAverage(c)).min(Comparator.comparingInt(c -> c.ordinal())); + int min = minAverage.orElse(Average.Hard).ordinal(); + + for (int i = 0; i < candidateErroneousCheckers.size(); i++) { + JsonSchemaAnnotatorChecker checker = candidateErroneousCheckers.get(i); + if (getAverage(checker).ordinal() <= min) { + current = candidateErroneousSchemas.get(i); + for (Map.Entry entry: checker.getErrors().entrySet()) { + error(entry.getKey(), entry.getValue()); + } } } + + if (current == null) { + current = ContainerUtil.getLastItem(candidateErroneousSchemas); + } + + //noinspection ConstantConditions return current; } diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonValidationError.java b/json/src/com/jetbrains/jsonSchema/impl/JsonValidationError.java index 25ab24a9ac31..ed81a3ef2479 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonValidationError.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonValidationError.java @@ -7,6 +7,7 @@ import com.jetbrains.jsonSchema.extension.JsonLikePsiWalker; import com.jetbrains.jsonSchema.impl.fixes.AddMissingPropertyFix; import com.jetbrains.jsonSchema.impl.fixes.RemoveProhibitedPropertyFix; import com.jetbrains.jsonSchema.impl.fixes.SuggestEnumValuesFix; +import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.Nullable; import java.util.Collection; @@ -19,6 +20,11 @@ public class JsonValidationError { return myIssueData; } + @MagicConstant(flagsFromClass = JsonValidationError.class) + public int getPriority() { + return myPriority; + } + public enum FixableIssueKind { MissingProperty, ProhibitedProperty, @@ -90,14 +96,21 @@ public class JsonValidationError { } } + public static final int LOW_PRIORITY = 3; + public static final int MEDIUM_PRIORITY = 2; + public static final int HIGH_PRIORITY = 1; + private final String myMessage; private final FixableIssueKind myFixableIssueKind; private final IssueData myIssueData; + private final int myPriority; - public JsonValidationError(String message, FixableIssueKind fixableIssueKind, IssueData issueData) { + public JsonValidationError(String message, FixableIssueKind fixableIssueKind, IssueData issueData, + @MagicConstant(flagsFromClass = JsonValidationError.class) int priority) { myMessage = message; myFixableIssueKind = fixableIssueKind; myIssueData = issueData; + myPriority = priority; } public String getMessage() { diff --git a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java index 06938b5888ca..e6edcffb01a6 100644 --- a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java +++ b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java @@ -841,4 +841,28 @@ public class JsonSchemaHighlightingTest extends JsonSchemaHighlightingTestBase { " }\n" + "}"); } + + public void testOneOfBestChoiceSchema() throws Exception { + @Language("JSON") String schemaText = FileUtil.loadFile(new File(getTestDataPath() + "/oneOfBestChoiceSchema.json")); + doTest(schemaText, "{\n" + + " \"results\": [\n" + + " {\n" + + " \"type\": \"person\"\n" + + " }\n" + + " ]\n" + + "}"); + } + + public void testAnyOfBestChoiceSchema() throws Exception { + @Language("JSON") String schemaText = FileUtil.loadFile(new File(getTestDataPath() + "/anyOfBestChoiceSchema.json")); + doTest(schemaText, "[\n" + + " {\n" + + " \"directory\": \"/test\",\n" + + " \"arguments\": [\n" + + " \"a\"\n" + + " ],\n" + + " \"file\": \"\"\n" + + " }\n" + + "] "); + } } diff --git a/json/tests/testData/jsonSchema/highlighting/anyOfBestChoiceSchema.json b/json/tests/testData/jsonSchema/highlighting/anyOfBestChoiceSchema.json new file mode 100644 index 000000000000..279d40c574b9 --- /dev/null +++ b/json/tests/testData/jsonSchema/highlighting/anyOfBestChoiceSchema.json @@ -0,0 +1,76 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://clang.llvm.org/docs/JSONCompilationDatabase.html!", + "title": "Compilation Database", + "description": "List of compilation commands, that specify each translation unit in a project", + "type": "array", + + "items": { + "$ref": "#/definitions/commandObject" + }, + + "definitions": { + "commandObject": { + "description": "Compilation command for single translation unit", + "type": "object", + + "properties": { + + "directory": { + "description": "Working directory of the compilation", + "type": "string", + "minLength": 1, + "examples": [ + "/home/user" + ] + }, + + "file": { + "description": "Main translation unit source processed by this compilation command", + "type": "string", + "minLength": 1, + "examples": [ + "main.cpp" + ] + }, + + "command": { + "description": "Compilation command", + "type": "string", + "minLength": 1, + "examples": [ + "/usr/bin/gcc main.cpp" + ] + }, + + "arguments": { + "description": "Compilation command as list of strings", + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 1 + }, + "examples": [ + ["/usr/bin/gcc", "main.cpp"] + ] + }, + + "output": { + "description": "Name of the output created by this compilation command", + "type": "string" + } + }, + + "required": ["directory", "file"], + "anyOf": [ + { + "required": ["command"] + }, + { + "required": ["arguments"] + } + ] + } + } +} diff --git a/json/tests/testData/jsonSchema/highlighting/oneOfBestChoiceSchema.json b/json/tests/testData/jsonSchema/highlighting/oneOfBestChoiceSchema.json new file mode 100644 index 000000000000..61c57f0076f6 --- /dev/null +++ b/json/tests/testData/jsonSchema/highlighting/oneOfBestChoiceSchema.json @@ -0,0 +1,36 @@ +{ + "type": "object", + "required": [ "results" ], + "properties": { + "results": { + "type": "array", + "items": { + "oneOf": [ + { "$ref": "#/definitions/person" }, + { "$ref": "#/definitions/company" } + ] + } + } + }, + + "definitions": { + "person": { + "type": "object", + "properties": { + "type": { "enum": [ "person" ] }, + "name": {"type": "string" }, + "dateOfBirth": {"type":"string"} + }, + "required": [ "type", "name", "dateOfBirth" ], + "additionalProperties": false + }, + + "company": { + "type": "object", + "properties": { + "type": { "enum": [ "company" ] } + }, + "additionalProperties": false + } + } +}