WEB-33724, WEB-33306 - json: better handle anyOf and oneOf

select the least erroneous schema to show errors when none of candidates has match
least erroneous option should at most correspond to what the user wanted to do
This commit is contained in:
Anton Lobov
2018-07-05 17:03:06 +02:00
parent a69191dd80
commit 782d513f66
5 changed files with 280 additions and 71 deletions
@@ -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<String, List<String>> 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<JsonValueAdapter> 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<String, Collection<JsonValueAdapter>> 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<JsonSchemaObject> oneOf) {
final Map<PsiElement, JsonValidationError> errors = new HashMap<>();
final List<JsonSchemaAnnotatorChecker> candidateErroneousCheckers = ContainerUtil.newArrayList();
final List<JsonSchemaObject> candidateErroneousSchemas = ContainerUtil.newArrayList();
boolean wasTypeError = false;
final List<JsonSchemaObject> 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<PsiElement, JsonValidationError> entry : errors.entrySet()) {
error(entry.getKey(), entry.getValue());
}
}
}
return current;
return showErrorsAndGetLeastErroneous(candidateErroneousCheckers, candidateErroneousSchemas);
}
private static boolean schemesDifferWithNotCheckedProperties(@NotNull final List<JsonSchemaObject> 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<JsonValidationError> 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<JsonSchemaObject> anyOf) {
final Map<PsiElement, JsonValidationError> errors = new HashMap<>();
JsonSchemaObject current = null;
final List<JsonSchemaAnnotatorChecker> candidateErroneousCheckers = ContainerUtil.newArrayList();
final List<JsonSchemaObject> 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<PsiElement, JsonValidationError> 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<JsonSchemaAnnotatorChecker> candidateErroneousCheckers,
@NotNull List<JsonSchemaObject> candidateErroneousSchemas) {
JsonSchemaObject current = null;
Optional<Average> 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<PsiElement, JsonValidationError> entry: checker.getErrors().entrySet()) {
error(entry.getKey(), entry.getValue());
}
}
}
if (current == null) {
current = ContainerUtil.getLastItem(candidateErroneousSchemas);
}
//noinspection ConstantConditions
return current;
}
@@ -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() {
@@ -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" +
" <warning descr=\"Missing required properties 'name', 'dateOfBirth'\">{\n" +
" \"type\": \"person\"\n" +
" }</warning>\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\": <warning>\"\"</warning>\n" +
" }\n" +
"] ");
}
}
@@ -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"]
}
]
}
}
}
@@ -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
}
}
}