WEB-34888 Validation of 'allOf' condition is too weak

This commit is contained in:
Anton Lobov
2018-09-19 13:37:27 +02:00
parent 9695e2d02f
commit 39bc5a4966
7 changed files with 103 additions and 26 deletions
@@ -2,6 +2,7 @@
package com.jetbrains.jsonSchema.impl;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.intellij.json.psi.JsonContainer;
import com.intellij.openapi.diagnostic.Logger;
@@ -107,6 +108,12 @@ public class JsonSchemaObject {
@Nullable private JsonSchemaObject myElse;
private boolean myShouldValidateAgainstJSType;
public boolean isValidByExclusion() {
return myIsValidByExclusion;
}
private boolean myIsValidByExclusion = true;
public JsonSchemaObject(@NotNull JsonContainer object) {
myJsonObject = object;
myProperties = new HashMap<>();
@@ -117,6 +124,89 @@ public class JsonSchemaObject {
myProperties = new HashMap<>();
}
@Nullable
private static JsonSchemaType getSubtypeOfBoth(@NotNull JsonSchemaType selfType,
@NotNull JsonSchemaType otherType) {
if (otherType == JsonSchemaType._any) return selfType;
if (selfType == JsonSchemaType._any) return otherType;
//noinspection EnumSwitchStatementWhichMissesCases
switch (selfType) {
case _string:
return otherType == JsonSchemaType._string || otherType == JsonSchemaType._string_number ? JsonSchemaType._string : null;
case _number:
if (otherType == JsonSchemaType._integer) return JsonSchemaType._integer;
return otherType == JsonSchemaType._number || otherType == JsonSchemaType._string_number ? JsonSchemaType._number : null;
case _integer:
return otherType == JsonSchemaType._number
|| otherType == JsonSchemaType._string_number
|| otherType == JsonSchemaType._integer ? JsonSchemaType._integer : null;
case _object:
return otherType == JsonSchemaType._object ? JsonSchemaType._object : null;
case _array:
return otherType == JsonSchemaType._array ? JsonSchemaType._array : null;
case _boolean:
return otherType == JsonSchemaType._boolean ? JsonSchemaType._boolean : null;
case _null:
return otherType == JsonSchemaType._null ? JsonSchemaType._null : null;
case _string_number:
return otherType == JsonSchemaType._integer
|| otherType == JsonSchemaType._number
|| otherType == JsonSchemaType._string
|| otherType == JsonSchemaType._string_number ? otherType : null;
}
return otherType;
}
@Nullable
private JsonSchemaType mergeTypes(@Nullable JsonSchemaType selfType,
@Nullable JsonSchemaType otherType,
@Nullable Set<JsonSchemaType> otherTypeVariants) {
if (selfType == null) return otherType;
if (otherType == null) {
if (otherTypeVariants != null && !otherTypeVariants.isEmpty()) {
Set<JsonSchemaType> filteredVariants = ContainerUtil.newHashSet(otherTypeVariants.size());
for (JsonSchemaType variant : otherTypeVariants) {
JsonSchemaType subtype = getSubtypeOfBoth(selfType, variant);
if (subtype != null) filteredVariants.add(subtype);
}
if (filteredVariants.size() == 0) {
myIsValidByExclusion = false;
return selfType;
}
if (filteredVariants.size() == 1) {
return filteredVariants.iterator().next();
}
return null; // will be handled by variants
}
return selfType;
}
JsonSchemaType subtypeOfBoth = getSubtypeOfBoth(selfType, otherType);
if (subtypeOfBoth == null){
myIsValidByExclusion = false;
return otherType;
}
return subtypeOfBoth;
}
private Set<JsonSchemaType> mergeTypeVariantSets(@Nullable Set<JsonSchemaType> self, @Nullable Set<JsonSchemaType> other) {
if (self == null) return other;
if (other == null) return self;
Set<JsonSchemaType> resultSet = ContainerUtil.newHashSet(self.size());
for (JsonSchemaType type : self) {
JsonSchemaType merged = mergeTypes(type, null, other);
if (merged != null) resultSet.add(merged);
}
if (resultSet.isEmpty()) {
myIsValidByExclusion = false;
return other;
}
return resultSet;
}
// peer pointer is not merged!
public void mergeValues(@NotNull JsonSchemaObject other) {
// we do not copy id, schema
@@ -136,11 +226,12 @@ public class JsonSchemaObject {
myHtmlDescription = other.myHtmlDescription;
}
if (other.myType != null) myType = other.myType;
myType = mergeTypes(myType, other.myType, other.myTypeVariants);
if (other.myDefault != null) myDefault = other.myDefault;
if (other.myRef != null) myRef = other.myRef;
if (other.myFormat != null) myFormat = other.myFormat;
myTypeVariants = copySet(myTypeVariants, other.myTypeVariants);
myTypeVariants = mergeTypeVariantSets(myTypeVariants, other.myTypeVariants);
if (other.myMultipleOf != null) myMultipleOf = other.myMultipleOf;
if (other.myMaximum != null) myMaximum = other.myMaximum;
if (other.myExclusiveMaximumNumber != null) myExclusiveMaximumNumber = other.myExclusiveMaximumNumber;
@@ -208,15 +299,6 @@ public class JsonSchemaObject {
return target;
}
@Nullable
private static <T> Set<T> copySet(@Nullable Set<T> target, @Nullable Set<T> source) {
if (source == null || source.isEmpty()) return target;
if (target != null && source.containsAll(target)) return target;
if (target == null) target = ContainerUtil.newHashSet(source.size());
target.addAll(source);
return target;
}
@Nullable
private static <K, V> Map<K, V> copyMap(@Nullable Map<K, V> target, @Nullable Map<K, V> source) {
if (source == null || source.isEmpty()) return target;
@@ -660,7 +742,7 @@ public class JsonSchemaObject {
private static String unescapeJsonString(@NotNull final String text) {
try {
final String object = String.format("{\"prop\": \"%s\"}", text);
return new Gson().fromJson(object, com.google.gson.JsonObject.class).get("prop").getAsString();
return new Gson().fromJson(object, JsonObject.class).get("prop").getAsString();
} catch (JsonParseException e) {
return text;
}
@@ -308,7 +308,10 @@ public class JsonSchemaVariantsTreeBuilder {
private static List<JsonSchemaObject> andGroup(@NotNull JsonSchemaObject object, @NotNull List<JsonSchemaObject> group) {
List<JsonSchemaObject> list = ContainerUtil.newArrayListWithCapacity(group.size());
for (JsonSchemaObject s: group) {
list.add(merge(object, s, s));
JsonSchemaObject schemaObject = merge(object, s, s);
if (schemaObject.isValidByExclusion()) {
list.add(schemaObject);
}
}
return list;
}
@@ -369,6 +372,7 @@ public class JsonSchemaVariantsTreeBuilder {
}
}
@NotNull
public static JsonSchemaObject merge(@NotNull JsonSchemaObject base,
@NotNull JsonSchemaObject other,
@NotNull JsonSchemaObject pointTo) {
@@ -99,7 +99,7 @@ public class JsonSchemaReSharperHighlightingTest extends JsonSchemaHighlightingT
public void test012() throws Exception {
doTestFiles("test012", "schema012");
}
public void _test012_2() throws Exception { // TODO bug
public void test012_2() throws Exception {
doTestFiles("test012_2", "schema012");
}
public void test012_3() throws Exception {
@@ -34,6 +34,7 @@ public class JsonSchemaTestSuite extends TestCase {
suite.addTestSuite(JsonSchemaCrossReferencesTest.class);
suite.addTestSuite(JsonSchemaDocumentationTest.class);
suite.addTestSuite(JsonSchemaHighlightingTest.class);
suite.addTestSuite(JsonSchemaReSharperHighlightingTest.class);
suite.addTestSuite(JsonSchemaPatternComparatorTest.class);
suite.addTestSuite(JsonSchemaSelfHighligthingTest.class);
suite.addTestSuite(JsonBySchemaCompletionTest.class);
@@ -1,10 +0,0 @@
{
"a": |true|(0)
}
---------------------------------------------------------
(0): ReSharper Underlined Error Highlighting: JSON validation failed: Should satisfy at least one of constraints:
Expression must be of type 'string'
Expression must be of type 'number'
@@ -1,5 +1,5 @@
{
"a": <warning descr="Type is not allowed. Expected one of: boolean, string.">5</warning>
"a": <warning descr="Type is not allowed. Expected: string.">5</warning>
}
@@ -1,5 +1,5 @@
{
"a": true
"a": <warning descr="Type is not allowed. Expected: string.">true</warning>
}