WEB-29954 Support JSON schema v7

This commit is contained in:
Anton Lobov
2018-04-17 15:32:28 +02:00
parent 3c3225761e
commit 2d815625f7
12 changed files with 472 additions and 17 deletions
@@ -16,15 +16,17 @@ import java.util.List;
* @author Irina.Chernushina on 2/24/2016.
*/
public class JsonSchemaProjectSelfProviderFactory implements JsonSchemaProviderFactory {
public static final int TOTAL_PROVIDERS = 2;
public static final String SCHEMA_JSON_FILE_NAME = "schema.json";
public static final String SCHEMA06_JSON_FILE_NAME = "schema06.json";
public static final int TOTAL_PROVIDERS = 3;
private static final String SCHEMA_JSON_FILE_NAME = "schema.json";
private static final String SCHEMA06_JSON_FILE_NAME = "schema06.json";
private static final String SCHEMA07_JSON_FILE_NAME = "schema07.json";
@NotNull
@Override
public List<JsonSchemaFileProvider> getProviders(@NotNull final Project project) {
return ContainerUtil.list(new MyJsonSchemaFileProvider(project, SCHEMA_JSON_FILE_NAME),
new MyJsonSchemaFileProvider(project, SCHEMA06_JSON_FILE_NAME));
new MyJsonSchemaFileProvider(project, SCHEMA06_JSON_FILE_NAME),
new MyJsonSchemaFileProvider(project, SCHEMA07_JSON_FILE_NAME));
}
public static class MyJsonSchemaFileProvider implements JsonSchemaFileProvider {
@@ -38,6 +40,9 @@ public class JsonSchemaProjectSelfProviderFactory implements JsonSchemaProviderF
public boolean isSchemaV6() {
return SCHEMA06_JSON_FILE_NAME.equals(myFileName);
}
public boolean isSchemaV7() {
return SCHEMA07_JSON_FILE_NAME.equals(myFileName);
}
private MyJsonSchemaFileProvider(@NotNull final Project project, @NotNull String fileName) {
myProject = project;
@@ -55,6 +60,8 @@ public class JsonSchemaProjectSelfProviderFactory implements JsonSchemaProviderF
return isSchemaV4();
case SCHEMA_6:
return isSchemaV6();
case SCHEMA_7:
return isSchemaV7();
}
throw new NotImplementedError("Unknown schema version: " + schemaVersion);
@@ -62,7 +69,7 @@ public class JsonSchemaProjectSelfProviderFactory implements JsonSchemaProviderF
@Override
public JsonSchemaVersion getSchemaVersion() {
return isSchemaV4() ? JsonSchemaVersion.SCHEMA_4 : JsonSchemaVersion.SCHEMA_6;
return isSchemaV4() ? JsonSchemaVersion.SCHEMA_4 : isSchemaV7() ? JsonSchemaVersion.SCHEMA_7 : JsonSchemaVersion.SCHEMA_6;
}
@NotNull
@@ -25,7 +25,7 @@ public class JsonCachedValues {
@Nullable
public static JsonSchemaObject getSchemaObject(@NotNull VirtualFile schemaFile, @NotNull Project project) {
JsonFileResolver.startFetchingHttpFileIfNeeded(schemaFile);
final PsiFile psiFile = PsiManager.getInstance(project).findFile(schemaFile);
final PsiFile psiFile = resolveFile(schemaFile, project);
if (!(psiFile instanceof JsonFile)) return null;
return CachedValueProviderOnPsiFile.getOrCompute(psiFile, JsonCachedValues::computeSchemaObject, JSON_OBJECT_CACHE_KEY);
@@ -55,11 +55,17 @@ public class JsonCachedValues {
@Nullable
public static String getSchemaUrlFromSchemaProperty(@NotNull VirtualFile file,
@NotNull Project project) {
PsiFile psiFile = PsiManager.getInstance(project).findFile(file);
PsiFile psiFile = resolveFile(file, project);
return !(psiFile instanceof JsonFile) ? null : CachedValueProviderOnPsiFile
.getOrCompute(psiFile, JsonCachedValues::fetchSchemaUrl, SCHEMA_URL_KEY);
}
private static PsiFile resolveFile(@NotNull VirtualFile file,
@NotNull Project project) {
if (!file.isValid()) return null;
return PsiManager.getInstance(project).findFile(file);
}
@Nullable
private static String fetchSchemaUrl(@Nullable PsiFile f) {
if (!(f instanceof JsonFile)) return null;
@@ -78,7 +84,7 @@ public class JsonCachedValues {
public static String getSchemaId(@NotNull final VirtualFile schemaFile,
@NotNull final Project project) {
if (!schemaFile.isValid()) return null;
final PsiFile psiFile = PsiManager.getInstance(project).findFile(schemaFile);
final PsiFile psiFile = resolveFile(schemaFile, project);
if (!(psiFile instanceof JsonFile)) return null;
return CachedValueProviderOnPsiFile.getOrCompute(psiFile, JsonCachedValues::getSchemaId, SCHEMA_ID_CACHE_KEY);
}
@@ -110,7 +116,7 @@ public class JsonCachedValues {
public static List<Pair<Collection<String>, String>> getSchemaCatalog(@NotNull final VirtualFile catalog,
@NotNull final Project project) {
if (!catalog.isValid()) return null;
final PsiFile psiFile = PsiManager.getInstance(project).findFile(catalog);
final PsiFile psiFile = resolveFile(catalog, project);
if (!(psiFile instanceof JsonFile)) return null;
return CachedValueProviderOnPsiFile.getOrCompute(psiFile, JsonCachedValues::computeSchemaCatalog, SCHEMA_CATALOG_CACHE_KEY);
}
@@ -190,6 +190,33 @@ class JsonSchemaAnnotatorChecker {
final JsonSchemaAnnotatorChecker checker = checkByMatchResult(value, result);
if (checker == null || checker.isCorrect()) error("Validates against 'not' schema", value.getDelegate());
}
if (schema.getIf() != null) {
MatchResult result = new JsonSchemaResolver(schema.getIf()).detailedResolve();
if (result.mySchemas.isEmpty() && result.myExcludingSchemas.isEmpty()) return;
final JsonSchemaAnnotatorChecker checker = checkByMatchResult(value, result);
if (checker != null) {
if (checker.isCorrect()) {
JsonSchemaObject then = schema.getThen();
if (then == null) {
error("Validates against 'if' branch but no 'then' branch is present", value.getDelegate());
}
else {
checkObjectBySchemaRecordErrors(then, value);
}
}
else {
JsonSchemaObject schemaElse = schema.getElse();
if (schemaElse == null) {
error("Validates counter 'if' branch but no 'else' branch is present", value.getDelegate());
}
else {
checkObjectBySchemaRecordErrors(schemaElse, value);
}
}
}
}
}
private void checkObjectBySchemaRecordErrors(@NotNull JsonSchemaObject schema, @NotNull JsonValueAdapter object) {
@@ -32,6 +32,7 @@ import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.jsonSchema.extension.JsonLikePsiWalker;
import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider;
import com.jetbrains.jsonSchema.extension.SchemaType;
import com.jetbrains.jsonSchema.extension.adapters.JsonObjectValueAdapter;
import com.jetbrains.jsonSchema.extension.adapters.JsonPropertyAdapter;
import com.jetbrains.jsonSchema.ide.JsonSchemaService;
import org.jetbrains.annotations.NotNull;
@@ -142,9 +143,8 @@ public class JsonSchemaCompletionContributor extends CompletionContributor {
final JsonPropertyAdapter adapter = myWalker.getParentPropertyAdapter(myOriginalPosition);
final Map<String, JsonSchemaObject> schemaProperties = schema.getProperties();
schemaProperties.keySet().stream()
.filter(name -> !properties.contains(name) || adapter != null && name.equals(adapter.getName()))
.forEach(name -> addPropertyVariant(name, schemaProperties.get(name), hasValue, insertComma));
addAllPropertyVariants(insertComma, hasValue, properties, adapter, schemaProperties);
addIfThenElsePropertyNameVariants(schema, insertComma, hasValue, properties, adapter);
}
else {
suggestValues(schema);
@@ -156,6 +156,46 @@ public class JsonSchemaCompletionContributor extends CompletionContributor {
}
}
private void addIfThenElsePropertyNameVariants(@NotNull JsonSchemaObject schema,
boolean insertComma,
boolean hasValue,
@NotNull Collection<String> properties,
@Nullable JsonPropertyAdapter adapter) {
if (schema.getIf() == null) return;
JsonLikePsiWalker walker = JsonLikePsiWalker.getWalker(myPosition, schema);
JsonPropertyAdapter propertyAdapter = walker == null ? null : walker.getParentPropertyAdapter(myPosition);
if (propertyAdapter == null) return;
JsonObjectValueAdapter object = propertyAdapter.getParentObject();
if (object == null) return;
JsonSchemaAnnotatorChecker checker = new JsonSchemaAnnotatorChecker();
checker.checkByScheme(object, schema.getIf());
if (checker.isCorrect()) {
JsonSchemaObject then = schema.getThen();
if (then != null) {
addAllPropertyVariants(insertComma, hasValue, properties, adapter, then.getProperties());
}
}
else {
JsonSchemaObject schemaElse = schema.getElse();
if (schemaElse != null) {
addAllPropertyVariants(insertComma, hasValue, properties, adapter, schemaElse.getProperties());
}
}
}
private void addAllPropertyVariants(boolean insertComma,
boolean hasValue,
Collection<String> properties,
JsonPropertyAdapter adapter,
Map<String, JsonSchemaObject> schemaProperties) {
schemaProperties.keySet().stream()
.filter(name -> !properties.contains(name) || adapter != null && name.equals(adapter.getName()))
.forEach(name -> addPropertyVariant(name, schemaProperties.get(name), hasValue, insertComma));
}
private void suggestValues(JsonSchemaObject schema) {
suggestValuesForSchemaVariants(schema.getAnyOf());
suggestValuesForSchemaVariants(schema.getOneOf());
@@ -89,6 +89,9 @@ public class JsonSchemaObject {
@Nullable private List<JsonSchemaObject> myAnyOf;
@Nullable private List<JsonSchemaObject> myOneOf;
@Nullable private JsonSchemaObject myNot;
@Nullable private JsonSchemaObject myIf;
@Nullable private JsonSchemaObject myThen;
@Nullable private JsonSchemaObject myElse;
private boolean myShouldValidateAgainstJSType;
public JsonSchemaObject(@NotNull JsonObject object) {
@@ -152,6 +155,9 @@ public class JsonSchemaObject {
myAnyOf = copyList(myAnyOf, other.myAnyOf);
myOneOf = copyList(myOneOf, other.myOneOf);
if (other.myNot != null) myNot = other.myNot;
if (other.myIf != null) myIf = other.myIf;
if (other.myThen != null) myThen = other.myThen;
if (other.myElse != null) myElse = other.myElse;
myShouldValidateAgainstJSType |= other.myShouldValidateAgainstJSType;
}
@@ -500,6 +506,33 @@ public class JsonSchemaObject {
myNot = not;
}
@Nullable
public JsonSchemaObject getIf() {
return myIf;
}
public void setIf(@Nullable JsonSchemaObject anIf) {
myIf = anIf;
}
@Nullable
public JsonSchemaObject getThen() {
return myThen;
}
public void setThen(@Nullable JsonSchemaObject then) {
myThen = then;
}
@Nullable
public JsonSchemaObject getElse() {
return myElse;
}
public void setElse(@Nullable JsonSchemaObject anElse) {
myElse = anElse;
}
@Nullable
public List<JsonSchemaType> getTypeVariants() {
return myTypeVariants;
@@ -190,10 +190,43 @@ public class JsonSchemaReader {
READERS_MAP.put("anyOf", createContainer((object, members) -> object.setAnyOf(members)));
READERS_MAP.put("oneOf", createContainer((object, members) -> object.setOneOf(members)));
READERS_MAP.put("not", createNot());
READERS_MAP.put("if", createIf());
READERS_MAP.put("then", createThen());
READERS_MAP.put("else", createElse());
READERS_MAP.put("instanceof", ((element, object, queue) -> object.shouldValidateAgainstJSType()));
READERS_MAP.put("typeof", ((element, object, queue) -> object.shouldValidateAgainstJSType()));
}
private static MyReader createIf() {
return (element, object, queue) -> {
if (element instanceof JsonObject) {
final JsonSchemaObject ifSchema = new JsonSchemaObject((JsonObject)element);
queue.add(ifSchema);
object.setIf(ifSchema);
}
};
}
private static MyReader createThen() {
return (element, object, queue) -> {
if (element instanceof JsonObject) {
final JsonSchemaObject ifSchema = new JsonSchemaObject((JsonObject)element);
queue.add(ifSchema);
object.setThen(ifSchema);
}
};
}
private static MyReader createElse() {
return (element, object, queue) -> {
if (element instanceof JsonObject) {
final JsonSchemaObject ifSchema = new JsonSchemaObject((JsonObject)element);
queue.add(ifSchema);
object.setElse(ifSchema);
}
};
}
private static MyReader createNot() {
return (element, object, queue) -> {
if (element instanceof JsonObject) {
@@ -369,7 +369,8 @@ public class JsonSchemaVariantsTreeBuilder {
}
private static boolean interestingSchema(@NotNull JsonSchemaObject schema) {
return schema.getAnyOf() != null || schema.getOneOf() != null || schema.getAllOf() != null || schema.getRef() != null;
return schema.getAnyOf() != null || schema.getOneOf() != null || schema.getAllOf() != null || schema.getRef() != null
|| schema.getIf() != null;
}
public static class Step {
@@ -450,8 +451,31 @@ public class JsonSchemaVariantsTreeBuilder {
if (schema != null) {
return Pair.create(ThreeState.UNSURE, schema);
}
if (parent.getAdditionalPropertiesSchema() != null && acceptAdditionalPropertiesSchemas) {
return Pair.create(ThreeState.UNSURE, parent.getAdditionalPropertiesSchema());
if (acceptAdditionalPropertiesSchemas) {
if (parent.getAdditionalPropertiesSchema() != null) {
return Pair.create(ThreeState.UNSURE, parent.getAdditionalPropertiesSchema());
}
// resolve inside V7 if-then-else conditionals
if (parent.getIf() != null) {
JsonSchemaObject childObject;
// NOTE: do not resolve inside 'if' itself - it is just a condition, but not an actual validation!
// only 'then' and 'else' branches provide actual validation sources, but not the 'if' branch
if (parent.getThen() != null) {
childObject = parent.getThen().getProperties().get(myName);
if (childObject != null) {
return Pair.create(ThreeState.UNSURE, childObject);
}
}
if (parent.getElse() != null) {
childObject = parent.getElse().getProperties().get(myName);
if (childObject != null) {
return Pair.create(ThreeState.UNSURE, childObject);
}
}
}
}
if (Boolean.FALSE.equals(parent.getAdditionalPropertiesAllowed())) {
return Pair.create(ThreeState.NO, null);
@@ -7,12 +7,15 @@ import org.jetbrains.annotations.Nullable;
public enum JsonSchemaVersion {
SCHEMA_4,
SCHEMA_6;
SCHEMA_6,
SCHEMA_7;
private static final String ourSchemaV4Schema = "http://json-schema.org/draft-04/schema#";
private static final String ourSchemaV4SchemaTrim = "http://json-schema.org/draft-04/schema";
private static final String ourSchemaV6Schema = "http://json-schema.org/draft-06/schema#";
private static final String ourSchemaV6SchemaTrim = "http://json-schema.org/draft-06/schema";
private static final String ourSchemaV7Schema = "http://json-schema.org/draft-07/schema#";
private static final String ourSchemaV7SchemaTrim = "http://json-schema.org/draft-07/schema";
@Override
public String toString() {
@@ -21,6 +24,8 @@ public enum JsonSchemaVersion {
return "JSON Schema Version 4";
case SCHEMA_6:
return "JSON Schema Version 6";
case SCHEMA_7:
return "JSON Schema Version 7";
}
throw new NotImplementedError("Unknown version: " + this);
@@ -36,12 +41,15 @@ public enum JsonSchemaVersion {
case ourSchemaV6Schema:
case ourSchemaV6SchemaTrim:
return SCHEMA_6;
case ourSchemaV7Schema:
case ourSchemaV7SchemaTrim:
return SCHEMA_7;
}
return null;
}
public static boolean isSchemaSchemaId(@Nullable String id) {
return ourSchemaV4Schema.equals(id) || ourSchemaV6Schema.equals(id);
return ourSchemaV4Schema.equals(id) || ourSchemaV6Schema.equals(id) || ourSchemaV7Schema.equals(id);
}
}
+168
View File
@@ -0,0 +1,168 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://json-schema.org/draft-07/schema#",
"title": "Core schema meta-schema",
"definitions": {
"schemaArray": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#" }
},
"nonNegativeInteger": {
"type": "integer",
"minimum": 0
},
"nonNegativeIntegerDefault0": {
"allOf": [
{ "$ref": "#/definitions/nonNegativeInteger" },
{ "default": 0 }
]
},
"simpleTypes": {
"enum": [
"array",
"boolean",
"integer",
"null",
"number",
"object",
"string"
]
},
"stringArray": {
"type": "array",
"items": { "type": "string" },
"uniqueItems": true,
"default": []
}
},
"type": ["object", "boolean"],
"properties": {
"$id": {
"type": "string",
"format": "uri-reference"
},
"$schema": {
"type": "string",
"format": "uri"
},
"$ref": {
"type": "string",
"format": "uri-reference"
},
"$comment": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"default": true,
"readOnly": {
"type": "boolean",
"default": false
},
"examples": {
"type": "array",
"items": true
},
"multipleOf": {
"type": "number",
"exclusiveMinimum": 0
},
"maximum": {
"type": "number"
},
"exclusiveMaximum": {
"type": "number"
},
"minimum": {
"type": "number"
},
"exclusiveMinimum": {
"type": "number"
},
"maxLength": { "$ref": "#/definitions/nonNegativeInteger" },
"minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
"pattern": {
"type": "string",
"format": "regex"
},
"additionalItems": { "$ref": "#" },
"items": {
"anyOf": [
{ "$ref": "#" },
{ "$ref": "#/definitions/schemaArray" }
],
"default": true
},
"maxItems": { "$ref": "#/definitions/nonNegativeInteger" },
"minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
"uniqueItems": {
"type": "boolean",
"default": false
},
"contains": { "$ref": "#" },
"maxProperties": { "$ref": "#/definitions/nonNegativeInteger" },
"minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
"required": { "$ref": "#/definitions/stringArray" },
"additionalProperties": { "$ref": "#" },
"definitions": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"default": {}
},
"properties": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"default": {}
},
"patternProperties": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"propertyNames": { "format": "regex" },
"default": {}
},
"dependencies": {
"type": "object",
"additionalProperties": {
"anyOf": [
{ "$ref": "#" },
{ "$ref": "#/definitions/stringArray" }
]
}
},
"propertyNames": { "$ref": "#" },
"const": true,
"enum": {
"type": "array",
"items": true,
"minItems": 1,
"uniqueItems": true
},
"type": {
"anyOf": [
{ "$ref": "#/definitions/simpleTypes" },
{
"type": "array",
"items": { "$ref": "#/definitions/simpleTypes" },
"minItems": 1,
"uniqueItems": true
}
]
},
"format": { "type": "string" },
"contentMediaType": { "type": "string" },
"contentEncoding": { "type": "string" },
"if": {"$ref": "#"},
"then": {"$ref": "#"},
"else": {"$ref": "#"},
"allOf": { "$ref": "#/definitions/schemaArray" },
"anyOf": { "$ref": "#/definitions/schemaArray" },
"oneOf": { "$ref": "#/definitions/schemaArray" },
"not": { "$ref": "#" }
},
"default": true
}
@@ -704,6 +704,44 @@ public class JsonSchemaHighlightingTest extends DaemonAnalyzerTestCase {
doTest(schema, "{\"prop\": \"foo\"}");
}
public void testIfThenElseV7() throws Exception {
@Language("JSON") String schema = "{\n" +
" \"if\": {\n" +
" \"properties\": {\n" +
" \"a\": {\n" +
" \"type\": \"string\"\n" +
" }\n" +
" },\n" +
" \"required\": [\"a\"]\n" +
" },\n" +
" \"then\": {\n" +
" \"properties\": {\n" +
" \"b\": {\n" +
" \"type\": \"number\"\n" +
" }\n" +
" },\n" +
" \"required\": [\"b\"]\n" +
" },\n" +
" \"else\": {\n" +
" \"properties\": {\n" +
" \"c\": {\n" +
" \"type\": \"boolean\"\n" +
" }\n" +
" },\n" +
" \"required\": [\"c\"]\n" +
" }\n" +
"}";
doTest(schema, "<warning>{}</warning>");
doTest(schema, "{\"c\": <warning>5</warning>}");
doTest(schema, "{\"c\": true}");
doTest(schema, "<warning>{\"a\": 5, \"b\": 5}</warning>");
doTest(schema, "{\"a\": 5, \"c\": <warning>5</warning>}");
doTest(schema, "{\"a\": 5, \"c\": true}");
doTest(schema, "<warning>{\"a\": \"a\", \"c\": true}</warning>");
doTest(schema, "{\"a\": \"a\", \"b\": <warning>true</warning>}");
doTest(schema, "{\"a\": \"a\", \"b\": 5}");
}
public void testNestedOneOf() throws Exception {
@Language("JSON") String schema = "{\"type\":\"object\",\n" +
" \"oneOf\": [\n" +
@@ -2,6 +2,7 @@ package com.jetbrains.jsonSchema.impl
import com.intellij.codeInsight.lookup.LookupElementPresentation
import com.jetbrains.jsonSchema.JsonSchemaHighlightingTest
import org.intellij.lang.annotations.Language
import org.junit.Assert
/**
@@ -284,4 +285,71 @@ class JsonBySchemaCompletionTest : JsonBySchemaCompletionBaseTest() {
vararg variants: String) {
testBySchema(schema, text, ".json", *variants)
}
private val ifThenElseSchema: String
get() {
@Suppress("UnnecessaryVariable")
@Language("JSON") val schema = """{
"if": {
"properties": {
"a": {
"type": "string"
}
},
"required": ["a"]
},
"then": {
"properties": {
"b": {
"type": "number",
"description": "Target b description"
}
},
"required": ["b"]
},
"else": {
"properties": {
"c": {
"type": "boolean",
"description": "Target c description"
}
},
"required": ["c"]
}
}"""
return schema
}
@Throws(Exception::class)
fun testIfThenElseV7EmptyPropName() {
testImpl(ifThenElseSchema, "{<caret>}", "\"c\"")
Assert.assertEquals(1, myItems.size.toLong())
val presentation = LookupElementPresentation()
myItems[0].renderElement(presentation)
Assert.assertEquals("Target c description", presentation.typeText)
}
@Throws(Exception::class)
fun testIfThenElseV7ThenPropName() {
testImpl(ifThenElseSchema, """{"a": "a", <caret>}""", "\"b\"")
Assert.assertEquals(1, myItems.size.toLong())
val presentation = LookupElementPresentation()
myItems[0].renderElement(presentation)
Assert.assertEquals("Target b description", presentation.typeText)
}
@Throws(Exception::class)
fun testIfThenElseV7ElsePropName() {
testImpl(ifThenElseSchema, """{"a": 5, <caret>}""", "\"c\"")
Assert.assertEquals(1, myItems.size.toLong())
val presentation = LookupElementPresentation()
myItems[0].renderElement(presentation)
Assert.assertEquals("Target c description", presentation.typeText)
}
@Throws(Exception::class)
fun testIfThenElseV7ElsePropValue() {
testImpl(ifThenElseSchema, """{"a": 5, "c": <caret>}""", "false", "true")
Assert.assertEquals(2, myItems.size.toLong())
}
}
@@ -219,6 +219,9 @@ class CommunityLibraryLicenses {
new LibraryLicense(name: "JSON Schema (schema06.json)", attachedTo: "intellij.json", version: "draft-06", license: "Simplified BSD License",
licenseUrl: "https://opensource.org/licenses/BSD-2-Clause",
url: "http://json-schema.org/draft-06/schema#"),
new LibraryLicense(name: "JSON Schema (schema07.json)", attachedTo: "intellij.json", version: "draft-07", license: "Simplified BSD License",
licenseUrl: "https://opensource.org/licenses/BSD-2-Clause",
url: "http://json-schema.org/draft-07/schema#"),
new LibraryLicense(name: "jsoup", libraryName: "jsoup", version: "1.10.3", license: "MIT",
url: "http://jsoup.org", licenseUrl: "http://jsoup.org/license"),
new LibraryLicense(name: "jsr305", libraryName: "jsr305", version: "snapshot", license: "BSD", url: "http://code.google.com/p/jsr-305/",