diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaAnnotatorChecker.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaAnnotatorChecker.java index 799ea2508ed9..0d9cfdaf76d9 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaAnnotatorChecker.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaAnnotatorChecker.java @@ -26,6 +26,7 @@ import com.intellij.psi.PsiElement; import com.intellij.util.ObjectUtils; import com.intellij.util.SmartList; import com.intellij.util.ThreeState; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import com.jetbrains.jsonSchema.extension.JsonLikePsiWalker; import com.jetbrains.jsonSchema.extension.adapters.JsonArrayValueAdapter; @@ -43,6 +44,8 @@ import java.util.stream.Collectors; * @author Irina.Chernushina on 4/25/2017. */ class JsonSchemaAnnotatorChecker { + private static final Set PRIMITIVE_TYPES = + ContainerUtil.set(JsonSchemaType._integer, JsonSchemaType._number, JsonSchemaType._boolean, JsonSchemaType._string, JsonSchemaType._null); private final Map myErrors; private boolean myHadTypeError; @@ -351,13 +354,12 @@ class JsonSchemaAnnotatorChecker { } private static boolean areSchemaTypesCompatible(@NotNull final JsonSchemaObject schema, @NotNull final JsonSchemaType type) { - if (getMatchingSchemaType(schema, type) != null) return true; - if (schema.getEnum() != null && (JsonSchemaType._integer.equals(type) || - JsonSchemaType._number.equals(type) || JsonSchemaType._boolean.equals(type) || - JsonSchemaType._string.equals(type))) { - return true; + if (schema.getEnum() != null) { + return PRIMITIVE_TYPES.contains(type); } - return false; + final JsonSchemaType matchingSchemaType = getMatchingSchemaType(schema, type); + if (matchingSchemaType != null) return matchingSchemaType.equals(type); + return true; } @Nullable @@ -550,7 +552,7 @@ class JsonSchemaAnnotatorChecker { private JsonSchemaObject processOneOf(@NotNull JsonValueAdapter value, List oneOf) { final Map errors = new HashMap<>(); boolean wasTypeError = false; - int cntCorrect = 0; + final List correct = new SmartList<>(); JsonSchemaObject current = null; for (JsonSchemaObject object : oneOf) { // skip it if something JS awaited, we do not process it currently @@ -562,7 +564,7 @@ class JsonSchemaAnnotatorChecker { if (checker.isCorrect()) { current = object; errors.clear(); - ++cntCorrect; + correct.add(object); } else { if (errors.isEmpty() || wasTypeError && !checker.isHadTypeError() || errors.size() > checker.getErrors().size()) { @@ -573,10 +575,15 @@ class JsonSchemaAnnotatorChecker { } } } - if (cntCorrect == 1) return current; - if (cntCorrect > 0) { + if (correct.size() == 1) return current; + if (correct.size() > 0) { final JsonSchemaType type = JsonSchemaType.getType(value); - if (type != null) error("Validates to more than one variant", value.getDelegate()); + 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()); + } + } } else { if (!errors.isEmpty()) { @@ -588,6 +595,10 @@ class JsonSchemaAnnotatorChecker { return current; } + private static boolean schemesDifferWithNotCheckedProperties(@NotNull final List list) { + return list.stream().anyMatch(s -> !StringUtil.isEmptyOrSpaces(s.getFormat())); + } + // returns the schema, selected for annotation private JsonSchemaObject processAnyOf(@NotNull JsonValueAdapter value, List anyOf) { final Map errors = new HashMap<>(); diff --git a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java index 7ff84fa0126f..62763ba31810 100644 --- a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java +++ b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java @@ -305,8 +305,8 @@ public class JsonSchemaHighlightingTest extends DaemonAnalyzerTestCase { final String subSchema1 = "{\"enum\": [1,2,3,4,5]}"; final String subSchema2 = "{\"type\": \"array\", \"items\": {\"properties\": {\"kilo\": {}}, \"additionalProperties\": false}}"; final String schema = "{\"properties\": {\"prop\": {\"oneOf\": [" + subSchema1 + ", " + subSchema2 + "]}}}"; - doTest(schema, "{\"prop\": [{\"kilo\": 20}]}"); - doTest(schema, "{\"prop\": 5}"); + //doTest(schema, "{\"prop\": [{\"kilo\": 20}]}"); + //doTest(schema, "{\"prop\": 5}"); doTest(schema, "{\"prop\": [{\"foxtrot\": 15, \"kilo\": 20}]}"); } @@ -512,6 +512,45 @@ public class JsonSchemaHighlightingTest extends DaemonAnalyzerTestCase { doTest(schema, "{\"not_type\": \"4a4\"}"); } + public void testDoNotMarkOneOfThatDiffersWithFormat() throws Exception { + final String schema = "{\n" + + "\n" + + " \"properties\": {\n" + + " \"withFormat\": {\n" + + " \"type\": \"string\"," + + " \"oneOf\": [\n" + + " {\n" + + " \"format\":\"hostname\"\n" + + " },\n" + + " {\n" + + " \"format\": \"ip4\"\n" + + " }\n" + + " ]\n" + + " }\n" + + " }\n" + + "}"; + doTest(schema, "{\"withFormat\": \"localhost\"}"); + } + + public void testAcceptSchemaWithoutType() throws Exception { + final String schema = "{\n" + + "\n" + + " \"properties\": {\n" + + " \"withFormat\": {\n" + + " \"oneOf\": [\n" + + " {\n" + + " \"format\":\"hostname\"\n" + + " },\n" + + " {\n" + + " \"format\": \"ip4\"\n" + + " }\n" + + " ]\n" + + " }\n" + + " }\n" + + "}"; + doTest(schema, "{\"withFormat\": \"localhost\"}"); + } + public static String rootObjectRedefinedSchema() { return "{\n" + " \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n" +