Commit ae11c27c authored by Anton Lobov's avatar Anton Lobov
Browse files

RUBY-23470, RUBY-23469 - YAML: don't treat numeric values as strings for...

RUBY-23470, RUBY-23469 - YAML: don't treat numeric values as strings for JSON-schema based validation
parent 0f7eaad5
Branches unavailable Tags unavailable
No related merge requests found
Showing with 1676 additions and 22 deletions
+1676 -22
......@@ -5,6 +5,7 @@ import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.jsonSchema.extension.adapters.JsonArrayValueAdapter;
import com.jetbrains.jsonSchema.extension.adapters.JsonObjectValueAdapter;
import com.jetbrains.jsonSchema.extension.adapters.JsonValueAdapter;
......@@ -13,9 +14,14 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.yaml.psi.YAMLAnchor;
import org.jetbrains.yaml.psi.YAMLValue;
import java.util.Set;
import java.util.regex.Pattern;
public class YamlGenericValueAdapter implements JsonValueAdapter {
@NotNull private static final Set<String> NULLS = ContainerUtil.set("null", "Null", "NULL", "~");
@NotNull private static final Set<String> BOOLS = ContainerUtil.set("true", "True", "TRUE", "false", "False", "FALSE");
@NotNull private static final Set<String> INFS = ContainerUtil.set(".inf", ".Inf", ".INF");
@NotNull private static final Set<String> NANS = ContainerUtil.set(".nan", ".NaN", ".NAN");
@NotNull private final YAMLValue myValue;
public YamlGenericValueAdapter(@NotNull YAMLValue value) {myValue = value;}
......@@ -37,8 +43,7 @@ public class YamlGenericValueAdapter implements JsonValueAdapter {
@Override
public boolean isStringLiteral() {
String text = getTextWithoutRefs();
return !hasNonStringTags(text); /*values should always validate as string*/
return !isNumberLiteral() && !isBooleanLiteral() && !isNull();
}
private String getTextWithoutRefs() {
......@@ -53,13 +58,6 @@ public class YamlGenericValueAdapter implements JsonValueAdapter {
return text.substring(range.getStartOffset()).trim();
}
private static boolean hasNonStringTags(@NotNull String text) {
return hasTag(text, "bool")
|| hasTag(text, "null")
|| hasTag(text, "int")
|| hasTag(text, "float");
}
private static boolean hasTag(@NotNull String text, @NotNull String tagName) {
return StringUtil.startsWith(text, "!!" + tagName);
}
......@@ -73,13 +71,13 @@ public class YamlGenericValueAdapter implements JsonValueAdapter {
@Override
public boolean isBooleanLiteral() {
String text = getTextWithoutRefs();
return "true".equals(text) || "false".equals(text) || hasTag(text, "bool");
return BOOLS.contains(text) || hasTag(text, "bool");
}
@Override
public boolean isNull() {
String text = getTextWithoutRefs();
return "null".equals(text) || hasTag(text, "null");
return NULLS.contains(text) || hasTag(text, "null");
}
@NotNull
......@@ -113,20 +111,47 @@ public class YamlGenericValueAdapter implements JsonValueAdapter {
// http://yaml.org/spec/1.2/spec.html#id2803828
private static boolean isInteger(@NotNull String s) {
if (s.length() == 0) return false;
if ("0".equals(s)) return true;
if ("0".equals(s) || "-0".equals(s) || "+0".equals(s)) return true;
if (hasTag(s, "int")) return true;
int startIndex = s.charAt(0) == '-' ? 1 : 0;
if (matchesInt(s)) return true;
return false;
}
private static boolean matchesInt(@NotNull String s) {
char charZero = s.charAt(0);
int startIndex = (charZero == '-' || charZero == '+') ? 1 : 0;
char baseSign = ' ';
boolean expectBase = false;
for (int i = startIndex; i < s.length(); ++i) {
if (i == startIndex && s.charAt(i) == '0') return false;
if (!Character.isDigit(s.charAt(i))) return false;
if (i == startIndex && s.charAt(i) == '0') {
if (startIndex != 0) return false;
expectBase = true;
continue;
}
if (i == startIndex + 1 && expectBase) {
char c = s.charAt(i);
if (c != 'o' && c != 'x') return false;
baseSign = c;
}
if (baseSign == ' ' && !Character.isDigit(s.charAt(i))) return false;
else if (baseSign == 'o' && !StringUtil.isOctalDigit(s.charAt(i))) return false;
else if (baseSign == 'x' && !StringUtil.isHexDigit(s.charAt(i))) return false;
}
return true;
}
// http://yaml.org/spec/1.2/spec.html#id2804092
private static boolean isFloat(@NotNull String s) {
if (".inf".equals(s) || "-.inf".equals(s) || ".nan".equals(s)) return true;
if (INFS.contains(trimSign(s)) || NANS.contains(s)) return true;
if (hasTag(s, "float")) return true;
return Pattern.matches("-?[1-9](\\.[0-9]*[1-9])?(e[-+][1-9][0-9]*)?", s);
return Pattern.matches("[-+]?[1-9](\\.[0-9]*[1-9])?([eE][-+][1-9][0-9]*)?", s);
}
@NotNull
private static String trimSign(@NotNull String s) {
if (s.isEmpty()) return s;
char c = s.charAt(0);
return c == '+' || c == '-' ? s.substring(1) : s;
}
}
......@@ -93,7 +93,7 @@ public class YamlByJsonSchemaHighlightingTest extends JsonSchemaHighlightingTest
" }\n" +
"}");
doTest(schema, "prop:\n - 101\n - 102");
doTest(schema, "prop:\n - <warning descr=\"Schema validation: Less than a minimum 18.0\">16</warning>");
doTest(schema, "prop:\n - <warning descr=\"Schema validation: Less than a minimum 18\">16</warning>");
doTest(schema, "prop:\n - <warning descr=\"Schema validation: Type is not allowed. Expected: number.\">test</warning>");
}
......@@ -125,7 +125,7 @@ public class YamlByJsonSchemaHighlightingTest extends JsonSchemaHighlightingTest
" \"type\": \"number\", \"minimum\": 18" +
" }, {\"type\" : \"string\"}]\n" +
"}");
doTest(schema, "prop:\n - 101\n - 102");
doTest(schema, "prop:\n - 101\n - <warning descr=\"Schema validation: Type is not allowed. Expected: string.\">102</warning>");
@Language("JSON") final String schema2 = schema("{\n" +
" \"type\": \"array\",\n" +
......@@ -133,7 +133,7 @@ public class YamlByJsonSchemaHighlightingTest extends JsonSchemaHighlightingTest
" \"type\": \"number\", \"minimum\": 18" +
" }, {\"type\" : \"string\"}],\n" +
"\"additionalItems\": false}");
doTest(schema2, "prop:\n - 101\n - 102\n - <warning descr=\"Schema validation: Additional items are not allowed\">additional</warning>");
doTest(schema2, "prop:\n - 101\n - <warning descr=\"Schema validation: Type is not allowed. Expected: string.\">102</warning>\n - <warning descr=\"Schema validation: Additional items are not allowed\">additional</warning>");
}
public void testArrayLength() throws Exception {
......@@ -328,8 +328,8 @@ public class YamlByJsonSchemaHighlightingTest extends JsonSchemaHighlightingTest
" }\n" +
"}";
doTest(schema,
"p1: 1\n" +
"p2: 3\n" +
"p1: <warning descr=\"Schema validation: Type is not allowed. Expected: string.\">1</warning>\n" +
"p2: <warning descr=\"Schema validation: Type is not allowed. Expected: string.\">3</warning>\n" +
"a2: auto!\n" +
"a1: <warning descr=\"Schema validation: Value should be one of: \\\"auto!\\\"\">moto!</warning>\n"
);
......@@ -790,4 +790,11 @@ public class YamlByJsonSchemaHighlightingTest extends JsonSchemaHighlightingTest
" }\n" +
"}\n";
}
public void testTravisPythonVersion() throws Exception {
@Language("JSON") String schema = FileUtil.loadFile(new File(getTestDataPath() + "/travis.schema.json"));
doTest(schema, "python: 3.5"); // validates as 'number'
doTest(schema, "python: 3.50"); // validates as 'string'
doTest(schema, "python: <warning descr=\"Schema validation: Type is not allowed. Expected one of: array, number, string.\">null</warning>");
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment