WEB-33880 JSON Schemas: false positive 'Validates to more than one variant' warning

This commit is contained in:
Anton Lobov
2018-07-24 14:16:31 +02:00
parent a1453fc3b8
commit cb7ebe7ebb
5 changed files with 141 additions and 18 deletions
@@ -540,7 +540,7 @@ class JsonSchemaAnnotatorChecker {
if (type != null) {
list.add(type);
} else {
final List<JsonSchemaType> variants = schema.getTypeVariants();
final Set<JsonSchemaType> variants = schema.getTypeVariants();
if (variants != null) {
list.addAll(variants);
}
@@ -575,7 +575,7 @@ class JsonSchemaAnnotatorChecker {
}
}
if (schema.getTypeVariants() != null) {
List<JsonSchemaType> matchTypes = schema.getTypeVariants();
Set<JsonSchemaType> matchTypes = schema.getTypeVariants();
if (matchTypes.contains(input)) {
return input;
}
@@ -583,7 +583,7 @@ class JsonSchemaAnnotatorChecker {
return input;
}
//nothing matches, lets return one of the list so that other heuristics does not match
return matchTypes.get(0);
return matchTypes.iterator().next();
}
if (!schema.getProperties().isEmpty() && JsonSchemaType._object.equals(input)) return JsonSchemaType._object;
return null;
@@ -10,14 +10,12 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.ContainerUtilRt;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
@@ -50,7 +48,7 @@ public class JsonSchemaObject {
@Nullable private Object myDefault;
@Nullable private String myRef;
@Nullable private String myFormat;
@Nullable private List<JsonSchemaType> myTypeVariants;
@Nullable private Set<JsonSchemaType> myTypeVariants;
@Nullable private Number myMultipleOf;
@Nullable private Number myMaximum;
private boolean myExclusiveMaximum;
@@ -123,7 +121,7 @@ public class JsonSchemaObject {
if (other.myDefault != null) myDefault = other.myDefault;
if (other.myRef != null) myRef = other.myRef;
if (other.myFormat != null) myFormat = other.myFormat;
myTypeVariants = copyList(myTypeVariants, other.myTypeVariants);
myTypeVariants = copySet(myTypeVariants, other.myTypeVariants);
if (other.myMultipleOf != null) myMultipleOf = other.myMultipleOf;
if (other.myMaximum != null) myMaximum = other.myMaximum;
if (other.myExclusiveMaximumNumber != null) myExclusiveMaximumNumber = other.myExclusiveMaximumNumber;
@@ -186,7 +184,16 @@ public class JsonSchemaObject {
@Nullable
private static <T> List<T> copyList(@Nullable List<T> target, @Nullable List<T> source) {
if (source == null || source.isEmpty()) return target;
if (target == null) target = new ArrayList<>();
if (target == null) target = ContainerUtil.newArrayListWithCapacity(source.size());
target.addAll(source);
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;
}
@@ -194,7 +201,7 @@ public class JsonSchemaObject {
@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;
if (target == null) target = new HashMap<>();
if (target == null) target = ContainerUtilRt.newHashMap(source.size());
target.putAll(source);
return target;
}
@@ -548,11 +555,11 @@ public class JsonSchemaObject {
}
@Nullable
public List<JsonSchemaType> getTypeVariants() {
public Set<JsonSchemaType> getTypeVariants() {
return myTypeVariants;
}
public void setTypeVariants(@Nullable List<JsonSchemaType> typeVariants) {
public void setTypeVariants(@Nullable Set<JsonSchemaType> typeVariants) {
myTypeVariants = typeVariants;
}
@@ -782,7 +789,7 @@ public class JsonSchemaObject {
JsonSchemaType type = getType();
if (type != null) return type.getDescription();
List<JsonSchemaType> possibleTypes = getTypeVariants();
Set<JsonSchemaType> possibleTypes = getTypeVariants();
String description = getTypesDescription(shortDesc, possibleTypes);
if (description != null) return description;
@@ -796,9 +803,9 @@ public class JsonSchemaObject {
}
@Nullable
static String getTypesDescription(boolean shortDesc, @Nullable List<JsonSchemaType> possibleTypes) {
static String getTypesDescription(boolean shortDesc, @Nullable Collection<JsonSchemaType> possibleTypes) {
if (possibleTypes == null || possibleTypes.size() == 0) return null;
if (possibleTypes.size() == 1) return possibleTypes.get(0).getDescription();
if (possibleTypes.size() == 1) return possibleTypes.iterator().next().getDescription();
if (possibleTypes.contains(JsonSchemaType._any)) return JsonSchemaType._any.getDescription();
Stream<String> typeDescriptions = possibleTypes.stream().map(t -> t.getDescription()).distinct().sorted();
@@ -270,9 +270,9 @@ public class JsonSchemaReader {
final JsonSchemaType type = parseType(StringUtil.unquoteString(element.getText()));
if (type != null) object.setType(type);
} else if (element instanceof JsonArray) {
final List<JsonSchemaType> typeList = ((JsonArray)element).getValueList().stream()
final Set<JsonSchemaType> typeList = ((JsonArray)element).getValueList().stream()
.filter(notEmptyString()).map(el -> parseType(StringUtil.unquoteString(el.getText())))
.filter(el -> el != null).collect(Collectors.toList());
.filter(el -> el != null).collect(Collectors.toSet());
if (!typeList.isEmpty()) object.setTypeVariants(typeList);
}
};
@@ -866,6 +866,16 @@ public class JsonSchemaHighlightingTest extends JsonSchemaHighlightingTestBase {
"] ");
}
public void testComplexOneOfSchema() throws Exception {
@Language("JSON") String schemaText = FileUtil.loadFile(new File(getTestDataPath() + "/complexOneOfSchema.json"));
doTest(schemaText, "{\n" +
" \"indentation\": \"tab\"\n" +
" }");
doTest(schemaText, "{\n" +
" \"indentation\": <warning>\"ttab\"</warning>\n" +
" }");
}
public void testEnumCasing() throws Exception {
@Language("JSON") String schema = "{\n" +
" \"type\": \"object\",\n" +
@@ -0,0 +1,106 @@
{
"properties": {
"indentation": {
"description": "Specify indentation",
"type": [
"null",
"integer",
"string",
"array"
],
"oneOf": [
{
"type": [
"null",
"integer"
]
},
{
"type": "string",
"enum": [
"tab",
[]
]
},
{
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {
"type": "integer"
}
},
{
"type": "array",
"minItems": 2,
"maxItems": 2,
"uniqueItems": true,
"items": {
"type": [
"integer",
"string",
"object"
],
"anyOf": [
{
"type": "integer"
},
{
"type": "string",
"enum": [
"tab",
{}
]
},
{
"type": "object",
"allOf": [
{
"$ref": "#/definitions/coreRule"
}
],
"properties": {
"indentInsideParens": {
"description": "If `true`, the closing brace of a block (rule or at-rule) will be expected at the same indentation level as the block's inner nodes",
"type": "string",
"enum": [
"twice",
"once-at-root-twice-in-block"
]
},
"except": {
"description": "Do not indent for these things",
"type": "array",
"uniqueItems": true,
"minItems": 1,
"items": {
"type": "string",
"enum": [
"block",
"param",
"value"
]
}
},
"ignore": {
"description": "Ignore the indentation inside parentheses",
"type": "array",
"uniqueItems": true,
"minItems": 1,
"items": {
"type": "string",
"enum": [
"inside-parens",
"param",
"value"
]
}
}
}
}
]
}
}
]
}
}