diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaObjectAnnotator.java b/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaObjectAnnotator.java index 67caf00eebca..9664fbf4866d 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaObjectAnnotator.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaObjectAnnotator.java @@ -49,7 +49,7 @@ class JsonBySchemaObjectAnnotator implements Annotator { final PsiFile psiFile = element.getContainingFile(); if (! (psiFile instanceof JsonFile)) return; - final JsonProperty firstProp = PsiTreeUtil.getParentOfType(element, JsonProperty.class, false); + final JsonProperty firstProp = getFirstProperty(element); if (firstProp == null) { checkRootObject(holder, element); return; @@ -90,8 +90,21 @@ class JsonBySchemaObjectAnnotator implements Annotator { } } + private static JsonProperty getFirstProperty(@NotNull PsiElement element) { + JsonProperty firstProp = PsiTreeUtil.getParentOfType(element, JsonProperty.class, false); + if (firstProp == null) { + final JsonObject firstObject = PsiTreeUtil.getParentOfType(element, JsonObject.class, false); + if (firstObject != null && firstObject.getParent() instanceof JsonValue) { + final List propertyList = firstObject.getPropertyList(); + if (!propertyList.isEmpty()) firstProp = propertyList.get(0); + } + } + return firstProp; + } + private void checkRootObject(@NotNull AnnotationHolder holder, PsiElement property) { - final JsonObject object = PsiTreeUtil.getParentOfType(property, JsonObject.class); + JsonValue object = PsiTreeUtil.getParentOfType(property, JsonObject.class); + if (object == null) object = PsiTreeUtil.getParentOfType(property, JsonArray.class); if (object != null) { final BySchemaChecker rootChecker = new BySchemaChecker(); @@ -373,10 +386,13 @@ class JsonBySchemaObjectAnnotator implements Annotator { return; } } - if (schema.getMultipleOf() != null) { - final double leftOver = value.doubleValue() % schema.getMultipleOf().doubleValue(); + final Number multipleOf = schema.getMultipleOf(); + if (multipleOf != null) { + final double leftOver = value.doubleValue() % multipleOf.doubleValue(); if (leftOver > 0.000001) { - error("Is not multiple of " + propValue.getText(), propValue); + final String multipleOfValue = String.valueOf(Math.abs(multipleOf.doubleValue() - multipleOf.intValue()) < 0.000001 ? + multipleOf.intValue() : multipleOf); + error("Is not multiple of " + multipleOfValue, propValue); return; } } diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaWalker.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaWalker.java index 901ab2430507..aba049f9b0d7 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaWalker.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaWalker.java @@ -60,7 +60,10 @@ public class JsonSchemaWalker { if (checkable == null) return; final boolean isName = isName(checkable); final List position = findPosition(checkable, isName); - if (position == null || position.isEmpty()) return; + if (position == null || position.isEmpty()) { + if (isName) consumer.consume(true, rootSchema); + return; + } extractSchemaVariants(consumer, rootSchema, isName, position); } @@ -73,8 +76,9 @@ public class JsonSchemaWalker { final Pair pair = queue.removeFirst(); final JsonSchemaObject schema = pair.getFirst(); + if (position.size() <= pair.getSecond()) return; final Step step = position.get(pair.getSecond()); - if (step.getTransition() == null || (pair.getSecond() == (position.size() - 1))) { + if (step.getTransition() == null) { consumer.consume(isName, schema); continue; } @@ -109,7 +113,8 @@ public class JsonSchemaWalker { step.getTransition().step(object, transitionResultConsumer); // nothing or anything does not contribute to competion if (transitionResultConsumer.getSchema() != null) { - queue.add(Pair.create(transitionResultConsumer.getSchema(), pair.getSecond() + 1)); + if ((pair.getSecond() + 1) >= position.size()) consumer.consume(isName, transitionResultConsumer.getSchema()); + else queue.add(Pair.create(transitionResultConsumer.getSchema(), pair.getSecond() + 1)); } } } @@ -159,6 +164,7 @@ public class JsonSchemaWalker { steps.add(new Step(StateType._value, null)); } PsiElement current = element; + //PsiElement current = element instanceof JsonProperty ? ((JsonProperty)element).getNameElement() : element; while (! (current instanceof PsiFile)) { final PsiElement position = current; current = current.getParent(); @@ -178,7 +184,16 @@ public class JsonSchemaWalker { final String propertyName = ((JsonProperty)current).getName(); current = current.getParent(); if (!(current instanceof JsonObject)) return null;//incorrect syntax? - steps.add(new Step(StateType._object, new PropertyTransition(propertyName))); + // if either value or not first in the chain - needed for completion variant + if (position != element || !isName) { + steps.add(new Step(StateType._object, new PropertyTransition(propertyName))); + } + } else if (current instanceof JsonObject && position instanceof JsonProperty) { + // if either value or not first in the chain - needed for completion variant + if (position != element || !isName) { + final String propertyName = ((JsonProperty)position).getName(); + steps.add(new Step(StateType._object, new PropertyTransition(propertyName))); + } } else if (current instanceof PsiFile) { break; } else { diff --git a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java index 62fd68856e5e..48402774ad14 100644 --- a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java +++ b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java @@ -50,7 +50,7 @@ public class JsonSchemaHighlightingTest extends DaemonAnalyzerTestCase { public void testNumberMultipleWrong() throws Exception { testImpl("{ \"properties\": { \"prop\": {\"type\": \"number\", \"multipleOf\": 2}}}", - "{ \"prop\": 3}"); + "{ \"prop\": 3}"); } public void testNumberMultipleCorrect() throws Exception { @@ -96,6 +96,27 @@ public class JsonSchemaHighlightingTest extends DaemonAnalyzerTestCase { testImpl(schema, "{\"prop\": [\"test\"]}"); } + public void testTopLevelArray() throws Exception { + final String schema = "{\n" + + " \"type\": \"array\",\n" + + " \"items\": {\n" + + " \"type\": \"number\", \"minimum\": 18" + + " }\n" + + "}"; + testImpl(schema, "[101, 102]"); + } + + public void testTopLevelObjectArray() throws Exception { + final String schema = "{\n" + + " \"type\": \"array\",\n" + + " \"items\": {\n" + + " \"type\": \"object\", \"properties\": {\"a\": {\"type\": \"number\"}}" + + " }\n" + + "}"; + testImpl(schema, "[{\"a\": true}]"); + testImpl(schema, "[{\"a\": 18}]"); + } + public void testArrayTuples1() throws Exception { final String schema = schema("{\n" + " \"type\": \"array\",\n" + diff --git a/json/tests/test/com/jetbrains/jsonSchema/impl/JsonBySchemaCompletionTest.java b/json/tests/test/com/jetbrains/jsonSchema/impl/JsonBySchemaCompletionTest.java index ad8271b78392..25bfde2dad17 100644 --- a/json/tests/test/com/jetbrains/jsonSchema/impl/JsonBySchemaCompletionTest.java +++ b/json/tests/test/com/jetbrains/jsonSchema/impl/JsonBySchemaCompletionTest.java @@ -10,7 +10,6 @@ import org.junit.Assert; import java.io.StringReader; import java.util.Collections; -import java.util.Comparator; import java.util.List; /** @@ -99,6 +98,59 @@ public class JsonBySchemaCompletionTest extends CompletionTestCase { testImpl(schema, "{\"second\": }", "33", "44"); } + public void testValueCompletion() throws Exception { + final String schema = "{\n" + + " \"properties\": {\n" + + " \"top\": {\n" + + " \"enum\": [\"test\", \"me\"]\n" + + " }\n" + + " }\n" + + "}"; + testImpl(schema, "{\"top\": }", "\"me\"", "\"test\""); + } + + public void testTopLevelArrayPropNameCompletion() throws Exception { + final String schema = parcelShopSchema(); + testImpl(schema, "[{}]", "\"address\""); + testImpl(schema, "[{\"address\": {}}]", "\"fax\"", "\"houseNumber\""); + testImpl(schema, "[{\"address\": {\"houseNumber\": }}]", "1", "2"); + } + + @NotNull + private static String parcelShopSchema() { + return "{\n" + + " \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n" + + "\n" + + " \"title\": \"parcelshop search response schema\",\n" + + "\n" + + " \"definitions\": {\n" + + " \"address\": {\n" + + " \"type\": \"object\",\n" + + " \"properties\": {\n" + + " \"houseNumber\": { \"type\": \"integer\", \"enum\": [1,2]},\n" + + " \"fax\": { \"$ref\": \"#/definitions/phone\" }\n" + + " }\n" + + " },\n" + + " \"phone\": {\n" + + " \"type\": \"object\",\n" + + " \"properties\": {\n" + + " \"countryPrefix\": { \"type\": \"string\" },\n" + + " \"number\": { \"type\": \"string\" }\n" + + " }\n" + + " }\n" + + " },\n" + + "\n" + + " \"type\": \"array\",\n" + + "\n" + + " \"items\": {\n" + + " \"type\": \"object\",\n" + + " \"properties\": {\n" + + " \"address\": { \"$ref\": \"#/definitions/address\" }\n" + + " }\n" + + " }\n" + + "}"; + } + private void testImpl(@NotNull final String schema, final @NotNull String text, final @NotNull String... variants) throws Exception { final int position = EditorTestUtil.getCaretPosition(text);