From df89d5d66e60c06f291bc5287244cc15b30e73e9 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Wed, 16 Apr 2014 08:07:01 +0200 Subject: [PATCH 01/40] Prepare next version Signed-off-by: Francis Galiegue --- project.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.gradle b/project.gradle index c74d710aa..31520b7a3 100644 --- a/project.gradle +++ b/project.gradle @@ -21,7 +21,7 @@ * Project-specific settings. Unfortunately we cannot put the name in there! */ group = "com.github.fge"; -version = "2.2.0"; +version = "2.2.1-SNAPSHOT"; sourceCompatibility = "1.6"; targetCompatibility = "1.6"; // defaults to sourceCompatibility From 83370f81a395c1e257aa12c3a035d35b392e383a Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Wed, 16 Apr 2014 09:01:49 +0200 Subject: [PATCH 02/40] Main: default to loading schemas using the current directory as a URI Easily done since we already ensured schema existence by building a list of canonical files from the arguments. See issue #94. Signed-off-by: Francis Galiegue --- src/main/java/com/github/fge/jsonschema/main/cli/Main.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/github/fge/jsonschema/main/cli/Main.java b/src/main/java/com/github/fge/jsonschema/main/cli/Main.java index 24cee3992..4a9e2b37f 100644 --- a/src/main/java/com/github/fge/jsonschema/main/cli/Main.java +++ b/src/main/java/com/github/fge/jsonschema/main/cli/Main.java @@ -132,7 +132,8 @@ else if (optionSet.has("quiet")) { throws IOException { final URITranslatorConfigurationBuilder builder - = URITranslatorConfiguration.newBuilder(); + = URITranslatorConfiguration.newBuilder() + .setNamespace(getCwd()); if (fakeRoot != null) builder.addPathRedirect(fakeRoot, getCwd()); final LoadingConfiguration cfg = LoadingConfiguration.newBuilder() @@ -175,6 +176,7 @@ private RetCode doValidation(final Reporter reporter, throws IOException, ProcessingException { final File schemaFile = files.remove(0); + final String uri = schemaFile.toURI().normalize().toString(); JsonNode node; node = MAPPER.readTree(schemaFile); @@ -183,7 +185,7 @@ private RetCode doValidation(final Reporter reporter, return SCHEMA_SYNTAX_ERROR; } - final JsonSchema schema = factory.getJsonSchema(node); + final JsonSchema schema = factory.getJsonSchema(uri); RetCode ret = ALL_OK, retcode; From 07bb3162a821740da3f15261606fa9edd752d92d Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Wed, 16 Apr 2014 17:29:46 +0200 Subject: [PATCH 03/40] Main: improve help Signed-off-by: Francis Galiegue --- .../main/cli/CustomHelpFormatter.java | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/github/fge/jsonschema/main/cli/CustomHelpFormatter.java b/src/main/java/com/github/fge/jsonschema/main/cli/CustomHelpFormatter.java index e9577f33c..cbb441803 100644 --- a/src/main/java/com/github/fge/jsonschema/main/cli/CustomHelpFormatter.java +++ b/src/main/java/com/github/fge/jsonschema/main/cli/CustomHelpFormatter.java @@ -20,6 +20,7 @@ package com.github.fge.jsonschema.main.cli; import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import joptsimple.HelpFormatter; import joptsimple.OptionDescriptor; @@ -33,8 +34,24 @@ final class CustomHelpFormatter implements HelpFormatter { - private static final String HELP_PREAMBLE - = "Syntax: java -jar jsonschema.jar [options] file [file...]"; + private static final List HELP_PREAMBLE = ImmutableList.of( + "Syntax:", + " java -jar jsonschema.jar [options] schema file [file...]", + " java -jar jsonschema.jar --syntax [options] schema [schema...]", + "", + "Options: " + ); + + private static final List HELP_POST + = ImmutableList.builder() + .add("") + .add("Exit codes:") + .add(" 0: validation successful;") + .add(" 1: exception occurred (appears on stderr)") + .add(" 2: command line syntax error (missing argument, etc)") + .add(" 100: one or more file(s) failed validation") + .add(" 101: one or more schema(s) is/are invalid") + .build(); private static final String LINE_SEPARATOR = System.getProperty("line.separator", "\n"); @@ -50,9 +67,7 @@ public String format(final Map options) final Set opts = new LinkedHashSet( options.values()); - lines.add(HELP_PREAMBLE); - lines.add(""); - lines.add("Options: "); + lines.addAll(HELP_PREAMBLE); final int helpIndex = lines.size(); StringBuilder sb; @@ -72,13 +87,7 @@ public String format(final Map options) lines.add(sb.toString()); } - lines.add(""); - lines.add("Exit codes:"); - lines.add("\t0: validation successful;"); - lines.add("\t1: exception occurred (appears on stderr)"); - lines.add("\t2: command line syntax error (missing argument, etc)"); - lines.add("\t100: one or more file(s) failed validation"); - lines.add("\t101: one or more schema(s) is/are invalid"); + lines.addAll(HELP_POST); return JOINER.join(lines) + LINE_SEPARATOR; } From 6d8d5d1dc0e099836335f238daf88cfbb9cb9527 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Wed, 16 Apr 2014 18:08:09 +0200 Subject: [PATCH 04/40] Improve help for main Signed-off-by: Francis Galiegue --- .../fge/jsonschema/main/cli/CustomHelpFormatter.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/github/fge/jsonschema/main/cli/CustomHelpFormatter.java b/src/main/java/com/github/fge/jsonschema/main/cli/CustomHelpFormatter.java index cbb441803..95c6eaa4a 100644 --- a/src/main/java/com/github/fge/jsonschema/main/cli/CustomHelpFormatter.java +++ b/src/main/java/com/github/fge/jsonschema/main/cli/CustomHelpFormatter.java @@ -51,6 +51,12 @@ final class CustomHelpFormatter .add(" 2: command line syntax error (missing argument, etc)") .add(" 100: one or more file(s) failed validation") .add(" 101: one or more schema(s) is/are invalid") + .add("") + .add("Note: by default, the URI of schemas you use in validation mode") + .add("(that is, when you don't use --syntax) is considered to be the") + .add("current working directory plus the filename. If your schemas") + .add("all have a common URI prefix in a top level \"id\", you can fake") + .add("that the current directory is that prefix using --fakeroot.") .build(); private static final String LINE_SEPARATOR @@ -76,7 +82,7 @@ public String format(final Map options) if (descriptor.representsNonOptions()) continue; final Collection names = descriptor.options(); - sb = new StringBuilder().append('\t') + sb = new StringBuilder().append(" ") .append(optionsToString(names)); if (descriptor.requiresArgument()) sb.append(" uri"); From 168af10ea2fec9783d22ec18aac04fcae5dc27b7 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Wed, 16 Apr 2014 18:08:42 +0200 Subject: [PATCH 05/40] 2.2.1 Signed-off-by: Francis Galiegue --- project.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.gradle b/project.gradle index 31520b7a3..fa771d583 100644 --- a/project.gradle +++ b/project.gradle @@ -21,7 +21,7 @@ * Project-specific settings. Unfortunately we cannot put the name in there! */ group = "com.github.fge"; -version = "2.2.1-SNAPSHOT"; +version = "2.2.1"; sourceCompatibility = "1.6"; targetCompatibility = "1.6"; // defaults to sourceCompatibility From 4a77a17682d3f43a5dbc87260279caf26a806255 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Wed, 16 Apr 2014 18:25:48 +0200 Subject: [PATCH 06/40] Prepare for next version In 2.2.1: Francis Galiegue (5): Prepare next version Main: default to loading schemas using the current directory as a URI Main: improve help Improve help for main 2.2.1 Signed-off-by: Francis Galiegue --- project.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.gradle b/project.gradle index fa771d583..7d915a7a2 100644 --- a/project.gradle +++ b/project.gradle @@ -21,7 +21,7 @@ * Project-specific settings. Unfortunately we cannot put the name in there! */ group = "com.github.fge"; -version = "2.2.1"; +version = "2.2.2-SNAPSHOT"; sourceCompatibility = "1.6"; targetCompatibility = "1.6"; // defaults to sourceCompatibility From a0a037264a9acbe0342905c3dae0df2e0d095922 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Thu, 17 Apr 2014 18:26:37 +0200 Subject: [PATCH 07/40] Fill release notes Signed-off-by: Francis Galiegue --- RELEASE-NOTES.md | 75 +++--------------------------------------------- 1 file changed, 4 insertions(+), 71 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 58ffbf555..7adccf976 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,75 +1,8 @@ -### 2.1.10 +### 2.2.1 -* Plug in hyperschema syntax support... -* Gradle 1.11. -* Add a main method. -* -core 1.1.11. +* Main now uses current working directory as default URI namespace. -### 2.1.9 +### 2.2.0 -* Fix bug with string length calculations: it is the number of Unicode code - points which matters, not the number of `char`s (issue #92). -* Depend on -core 1.1.10: schema sources with trailing input are now considered - illegal. -* Make all tests run from the command line. -* Small javadoc fixes. - -### 2.1.8 - -* Add "deep validation": validate children even if container fails -* -core update to 1.1.9: package changes (BREAKS OLDER CODE) -* Change licensing to dual LGPLv3/ ASL 2.0 -* Dependencies updates (Joda Time 2.3, libphonenumber 5.9) - -### 2.1.7 - -* Import all format attributes from - [json-schema-formats](https://github.com/fge/json-schema-formats). -* Switch to gradle for build. -* Major dependencies updates; drop ServiceLoader for message bundles. -* Fix javadoc generation. - -### 2.1.6 - -* Update json-schema-core dependency to 1.1.7. -* Fix a bug in pom.xml which would cause the service file to not be generated. -* Fix two places where core messages would not be fetched properly. - -### 2.1.5 - -* Update json-schema-core dependency to 1.1.6. -* Use [msg-simple](https://github.com/fge/msg-simple) for message bundles. -* Remove all unchecked exceptions, now unneeded. -* Improve, test all error messages. -* pom.xml improvements. -* Remove obsolete code. - -### 2.1.4 - -* Update -core dependency. -* Use resource bundles for all configuration/validation messages (issue #55). -* Modify pom.xml to allow OSGi-capable deployments (courtesy of Matt Bishop). - -### 2.1.3 - -* Modify date/time format checkings to accurately check for the required number - of digits - -### 2.1.2 - -* Update -core dependency to 1.1.3 -* Update libphonenumber dependency - -### 2.1.1 - -* Update -core dependency (including Guava), adapt code. -* Some error message rework. - -### 2.1.0 - -* Depend on -core 1.1.1. Change relevant code accordingly. -* Simplify failure code on syntax validation failure. -* Fix `date-time` format checking: up to three millisecond digits are allowed by -* ISO 8601. -* Joda Time dependency updated to 2.2. +* New major release. From 5e9242f688450d90eaa9bfaf896b6d178d88ed12 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Thu, 17 Apr 2014 18:27:05 +0200 Subject: [PATCH 08/40] Depend on -core 1.2.1 to fix Rhino bug Signed-off-by: Francis Galiegue --- project.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.gradle b/project.gradle index 7d915a7a2..acc16b334 100644 --- a/project.gradle +++ b/project.gradle @@ -34,7 +34,7 @@ project.ext { */ dependencies { compile(group: "com.github.fge", name: "json-schema-core", - version: "1.2.0"); + version: "1.2.1"); compile(group: "javax.mail", name: "mailapi", version: "1.4.3"); compile(group: "joda-time", name: "joda-time", version: "2.3"); compile(group: "com.googlecode.libphonenumber", name: "libphonenumber", From 828f1d6c1d7020599bb722f4c5447c5536f038a4 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Thu, 17 Apr 2014 18:27:31 +0200 Subject: [PATCH 09/40] 2.2.2 Signed-off-by: Francis Galiegue --- project.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.gradle b/project.gradle index acc16b334..d3e8b409d 100644 --- a/project.gradle +++ b/project.gradle @@ -21,7 +21,7 @@ * Project-specific settings. Unfortunately we cannot put the name in there! */ group = "com.github.fge"; -version = "2.2.2-SNAPSHOT"; +version = "2.2.2"; sourceCompatibility = "1.6"; targetCompatibility = "1.6"; // defaults to sourceCompatibility From ab3e324d34ec61d73810a8b5004bbaf554920a82 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Thu, 17 Apr 2014 18:36:06 +0200 Subject: [PATCH 10/40] Release notes completion Signed-off-by: Francis Galiegue --- RELEASE-NOTES.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 7adccf976..fbe0d9a5b 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,3 +1,12 @@ +### 2.2.3 + +* Re-release... 2.2.2 was compiled with JDK 8 :/ + +### 2.2.2 + +* Depend on -core 1.2.1 to work around Rhino bug with other packages sealing the + context. + ### 2.2.1 * Main now uses current working directory as default URI namespace. From e6a343100aede7c34f0b84b70ffac56bf63a11f3 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Thu, 17 Apr 2014 18:34:11 +0200 Subject: [PATCH 11/40] 2.2.3 Argh. Compiled 2.2.2 with Java 8. Signed-off-by: Francis Galiegue --- project.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.gradle b/project.gradle index d3e8b409d..ee3e9c3db 100644 --- a/project.gradle +++ b/project.gradle @@ -21,7 +21,7 @@ * Project-specific settings. Unfortunately we cannot put the name in there! */ group = "com.github.fge"; -version = "2.2.2"; +version = "2.2.3"; sourceCompatibility = "1.6"; targetCompatibility = "1.6"; // defaults to sourceCompatibility From dc9bf4b4d64655987cc72e32670480d5891a75a8 Mon Sep 17 00:00:00 2001 From: Pablo Pinheiro Date: Tue, 29 Apr 2014 09:23:41 -0300 Subject: [PATCH 12/40] Added a lib jar to the build --- build.gradle | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/build.gradle b/build.gradle index cc23d4051..90ab40c6d 100644 --- a/build.gradle +++ b/build.gradle @@ -128,11 +128,22 @@ task fullJar(type: OneJar) { archiveName = "jsonschema.jar"; } +/* + * Creates a jar that can be used as a library on java projects. + * This jar already includes all the dependencies. + */ +task libJar(type: Jar, dependsOn: jar) { + appendix = "lib" + from {configurations.compile.collect {zipTree(it)}} + with jar +} + artifacts { archives jar; archives sourcesJar; archives javadocJar; archives fullJar; + archives libJar; } task wrapper(type: Wrapper) { From 555c5874b7fabe5bfb8b8a2e22abb9df917cca68 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Fri, 23 May 2014 10:38:21 +0200 Subject: [PATCH 13/40] Fill in RELEASE-NOTES and CONTRIBUTORS Signed-off-by: Francis Galiegue --- CONTRIBUTORS.md | 4 ++++ RELEASE-NOTES.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index c4a831b4a..be0f72403 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,3 +1,7 @@ +### Pablo Pinhero + +* Add a "lib" target to the build: jar with all dependencies builtin. + ### Corey Sciuto * Tests for date-time format attribute; date-time-millis format attribute (now diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index fbe0d9a5b..48a147e2b 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,3 +1,7 @@ +### 2.2.4 (not released yet) + +* Add a "lib" target to the build + ### 2.2.3 * Re-release... 2.2.2 was compiled with JDK 8 :/ From 41066f2ae2dd4dc62409dcddb5c26cd428757a5a Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Fri, 23 May 2014 10:41:34 +0200 Subject: [PATCH 14/40] Bump version Signed-off-by: Francis Galiegue --- build.gradle | 4 +++- project.gradle | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 90ab40c6d..8d322f38e 100644 --- a/build.gradle +++ b/build.gradle @@ -134,7 +134,9 @@ task fullJar(type: OneJar) { */ task libJar(type: Jar, dependsOn: jar) { appendix = "lib" - from {configurations.compile.collect {zipTree(it)}} + from { + configurations.compile.collect { zipTree(it) } + } with jar } diff --git a/project.gradle b/project.gradle index ee3e9c3db..5f946dcb7 100644 --- a/project.gradle +++ b/project.gradle @@ -21,7 +21,7 @@ * Project-specific settings. Unfortunately we cannot put the name in there! */ group = "com.github.fge"; -version = "2.2.3"; +version = "2.2.4-SNAPSHOT"; sourceCompatibility = "1.6"; targetCompatibility = "1.6"; // defaults to sourceCompatibility From 44374baa9bd73bf723cc42ee7481c6412b380bcf Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Fri, 23 May 2014 11:48:05 +0200 Subject: [PATCH 15/40] Output syntax errors when throwing an InvalidSchemaException For this, depend on -core 1.2.3 (which fixes a missing key) and walk the previous report to search for syntax errors; append these to the ProcessingMessage we use to initialize the exception. Fixes issue #99. Signed-off-by: Francis Galiegue --- project.gradle | 2 +- .../validation/ValidationProcessor.java | 26 ++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/project.gradle b/project.gradle index 5f946dcb7..93fba1b2d 100644 --- a/project.gradle +++ b/project.gradle @@ -34,7 +34,7 @@ project.ext { */ dependencies { compile(group: "com.github.fge", name: "json-schema-core", - version: "1.2.1"); + version: "1.2.3"); compile(group: "javax.mail", name: "mailapi", version: "1.4.3"); compile(group: "joda-time", name: "joda-time", version: "2.3"); compile(group: "com.googlecode.libphonenumber", name: "libphonenumber", diff --git a/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationProcessor.java b/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationProcessor.java index 2e6dde82f..c824c14e8 100644 --- a/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationProcessor.java +++ b/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationProcessor.java @@ -20,6 +20,8 @@ package com.github.fge.jsonschema.processors.validation; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.github.fge.jackson.JacksonUtils; import com.github.fge.jackson.jsonpointer.JsonPointer; import com.github.fge.jsonschema.cfg.ValidationConfiguration; import com.github.fge.jsonschema.core.exceptions.InvalidSchemaException; @@ -83,9 +85,27 @@ public FullData process(final ProcessingReport report, */ final ValidatorList fullContext = processor.process(report, context); - if (fullContext == null) - throw new InvalidSchemaException(new ProcessingMessage() - .setMessage(syntaxMessages.getMessage("core.invalidSchema"))); + if (fullContext == null) { + /* + * OK, that's for issue #99 but that's ugly nevertheless. + * + * We want syntax error messages to appear in the exception text. + */ + final String msg = syntaxMessages.getMessage("core.invalidSchema"); + final ArrayNode arrayNode = JacksonUtils.nodeFactory().arrayNode(); + JsonNode node; + for (final ProcessingMessage message: report) { + node = message.asJson(); + if ("syntax".equals(node.path("domain").asText())) + arrayNode.add(node); + } + final StringBuilder sb = new StringBuilder(msg); + sb.append("\nSyntax errors:\n"); + sb.append(JacksonUtils.prettyPrint(arrayNode)); + final ProcessingMessage message = new ProcessingMessage() + .setMessage(sb.toString()); + throw new InvalidSchemaException(message); + } /* * Get the calculated context. Build the data. From 4440eb1d83f970174f4ef365cd89e75af65cd780 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Fri, 23 May 2014 11:54:48 +0200 Subject: [PATCH 16/40] Fill release notes Signed-off-by: Francis Galiegue --- RELEASE-NOTES.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 48a147e2b..65ae2fe46 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,6 +1,9 @@ ### 2.2.4 (not released yet) -* Add a "lib" target to the build +* Add a "lib" target to the build. +* Issue #99: append syntax errors when throwing an InvalidSchemaException. +* Issue #100: attempt to load resources from the context classloader if loading + from JsonLoader.class fails. ### 2.2.3 From 438e5169cf9964c73fb2413432173486da06996c Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Fri, 23 May 2014 12:00:09 +0200 Subject: [PATCH 17/40] Fix libJar task: add different classifier "appendix" is not enough. --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 8d322f38e..5f9fbc12c 100644 --- a/build.gradle +++ b/build.gradle @@ -133,11 +133,11 @@ task fullJar(type: OneJar) { * This jar already includes all the dependencies. */ task libJar(type: Jar, dependsOn: jar) { - appendix = "lib" + classifier = "lib"; from { configurations.compile.collect { zipTree(it) } - } - with jar + }; + with jar; } artifacts { From 71db68228b77f05b7f11849328006e2dcf080d47 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Fri, 23 May 2014 11:56:52 +0200 Subject: [PATCH 18/40] 2.2.4 Signed-off-by: Francis Galiegue --- RELEASE-NOTES.md | 2 +- project.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 65ae2fe46..a59a0c25d 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,4 +1,4 @@ -### 2.2.4 (not released yet) +### 2.2.4 * Add a "lib" target to the build. * Issue #99: append syntax errors when throwing an InvalidSchemaException. diff --git a/project.gradle b/project.gradle index 93fba1b2d..af77839dc 100644 --- a/project.gradle +++ b/project.gradle @@ -21,7 +21,7 @@ * Project-specific settings. Unfortunately we cannot put the name in there! */ group = "com.github.fge"; -version = "2.2.4-SNAPSHOT"; +version = "2.2.4"; sourceCompatibility = "1.6"; targetCompatibility = "1.6"; // defaults to sourceCompatibility From 4834e5b304029746feb74c79ccf8c7a8c54a3e78 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sat, 24 May 2014 10:34:16 +0200 Subject: [PATCH 19/40] Bump version number Signed-off-by: Francis Galiegue --- project.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.gradle b/project.gradle index af77839dc..7f9ac59b6 100644 --- a/project.gradle +++ b/project.gradle @@ -21,7 +21,7 @@ * Project-specific settings. Unfortunately we cannot put the name in there! */ group = "com.github.fge"; -version = "2.2.4"; +version = "2.2.5-SNAPSHOT"; sourceCompatibility = "1.6"; targetCompatibility = "1.6"; // defaults to sourceCompatibility From de1987804d837f4ce40bc4f95321aa27d3305567 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sat, 24 May 2014 13:05:57 +0200 Subject: [PATCH 20/40] Depend on local -SNAPSHOT Signed-off-by: Francis Galiegue --- build.gradle | 1 + project.gradle | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5f9fbc12c..5d67bc776 100644 --- a/build.gradle +++ b/build.gradle @@ -72,6 +72,7 @@ apply(from: "project.gradle"); */ repositories { mavenCentral(); + mavenLocal(); } /* diff --git a/project.gradle b/project.gradle index 7f9ac59b6..180ed8352 100644 --- a/project.gradle +++ b/project.gradle @@ -34,7 +34,7 @@ project.ext { */ dependencies { compile(group: "com.github.fge", name: "json-schema-core", - version: "1.2.3"); + version: "1.2.4-SNAPSHOT"); compile(group: "javax.mail", name: "mailapi", version: "1.4.3"); compile(group: "joda-time", name: "joda-time", version: "2.3"); compile(group: "com.googlecode.libphonenumber", name: "libphonenumber", From 2f5d63ab89cc3efaa102af6c614991f65296563f Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sat, 24 May 2014 10:06:35 +0200 Subject: [PATCH 21/40] Make test to reproduce issue #102 Signed-off-by: Francis Galiegue --- .../validation/ValidationProcessorTest.java | 21 +++++++++++-- src/test/resources/other/issue102.json | 30 +++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 src/test/resources/other/issue102.json diff --git a/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java b/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java index 65af8a23c..f51123c01 100644 --- a/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java +++ b/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java @@ -24,6 +24,7 @@ import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; import com.github.fge.jackson.JacksonUtils; +import com.github.fge.jackson.JsonLoader; import com.github.fge.jackson.NodeType; import com.github.fge.jsonschema.cfg.ValidationConfiguration; import com.github.fge.jsonschema.core.exceptions.ProcessingException; @@ -39,15 +40,18 @@ import com.github.fge.jsonschema.library.Keyword; import com.github.fge.jsonschema.library.Library; import com.github.fge.jsonschema.main.JsonSchemaFactory; +import com.github.fge.jsonschema.main.JsonValidator; import com.github.fge.jsonschema.processors.data.FullData; import com.github.fge.msgsimple.bundle.MessageBundle; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; -import static org.mockito.Mockito.*; -import static org.testng.Assert.*; +import static org.mockito.Mockito.mock; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; public final class ValidationProcessorTest { @@ -111,6 +115,19 @@ public void childrenAreExploredOnDemandEvenIfContainerFails() assertEquals(COUNT.get(), 1); } + @Test(timeOut = 1000) + public void circularReferencingDuringValidationIsDetected() + throws IOException, ProcessingException + { + final JsonNode schemaNode + = JsonLoader.fromResource("/other/issue102.json"); + final JsonSchemaFactory factory = JsonSchemaFactory.byDefault(); + final JsonValidator validator = factory.getValidator(); + + validator.validate(schemaNode, JacksonUtils.nodeFactory().nullNode()); + assertTrue(true); + } + public static final class K1Validator extends AbstractKeywordValidator { diff --git a/src/test/resources/other/issue102.json b/src/test/resources/other/issue102.json new file mode 100644 index 000000000..1af9832c3 --- /dev/null +++ b/src/test/resources/other/issue102.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "S": { + "allOf": [ + { + "type": "string", + "enum": ["a"] + }, + { + "oneOf": [ + { + "$ref": "#/definitions/S" + }, + { + "type": "string", + "enum": [""] + } + ] + }, + { + "type": "string", + "enum": ["b"] + } + ], + "additionalItems": false + } + }, + "$ref": "#/definitions/S" +} \ No newline at end of file From 0fafd3ea061c8f0fac7620ef662871cda6366918 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sat, 24 May 2014 13:15:19 +0200 Subject: [PATCH 22/40] SchemaContextEquivalence: remove reference to SchemaTreeEquivalence Use SchemaTree's equals()/hashCode() instead Signed-off-by: Francis Galiegue --- .../validation/SchemaContextEquivalence.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/github/fge/jsonschema/processors/validation/SchemaContextEquivalence.java b/src/main/java/com/github/fge/jsonschema/processors/validation/SchemaContextEquivalence.java index c978d41a6..8f07d7a68 100644 --- a/src/main/java/com/github/fge/jsonschema/processors/validation/SchemaContextEquivalence.java +++ b/src/main/java/com/github/fge/jsonschema/processors/validation/SchemaContextEquivalence.java @@ -19,8 +19,6 @@ package com.github.fge.jsonschema.processors.validation; -import com.github.fge.jsonschema.core.tree.SchemaTree; -import com.github.fge.jsonschema.core.util.equivalence.SchemaTreeEquivalence; import com.github.fge.jsonschema.processors.data.SchemaContext; import com.google.common.base.Equivalence; @@ -36,7 +34,6 @@ *
  • and the type of the instance is the same.
  • * * - * @see SchemaTreeEquivalence */ public final class SchemaContextEquivalence extends Equivalence @@ -44,9 +41,6 @@ public final class SchemaContextEquivalence private static final Equivalence INSTANCE = new SchemaContextEquivalence(); - private static final Equivalence TREE_EQUIVALENCE - = SchemaTreeEquivalence.getInstance(); - public static Equivalence getInstance() { return INSTANCE; @@ -55,14 +49,13 @@ public static Equivalence getInstance() @Override protected boolean doEquivalent(final SchemaContext a, final SchemaContext b) { - return TREE_EQUIVALENCE.equivalent(a.getSchema(), b.getSchema()) + return a.getSchema().equals(b.getSchema()) && a.getInstanceType() == b.getInstanceType(); } @Override protected int doHash(final SchemaContext t) { - return 31 * TREE_EQUIVALENCE.hash(t.getSchema()) - + t.getInstanceType().hashCode(); + return t.getSchema().hashCode() ^ t.getInstanceType().hashCode(); } } From afcbb4ea01a746eb74e4f919d7833ba5a45ecb92 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sat, 24 May 2014 13:29:08 +0200 Subject: [PATCH 23/40] ValidationChain: drop use of SchemaTreeEquivalence Signed-off-by: Francis Galiegue --- .../processors/validation/ValidationChain.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationChain.java b/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationChain.java index 65062b4a7..bc4e4ad9e 100644 --- a/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationChain.java +++ b/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationChain.java @@ -30,7 +30,6 @@ import com.github.fge.jsonschema.core.report.ProcessingReport; import com.github.fge.jsonschema.core.tree.SchemaTree; import com.github.fge.jsonschema.core.util.ValueHolder; -import com.github.fge.jsonschema.core.util.equivalence.SchemaTreeEquivalence; import com.github.fge.jsonschema.library.Library; import com.github.fge.jsonschema.processors.build.ValidatorBuilder; import com.github.fge.jsonschema.processors.data.SchemaContext; @@ -39,6 +38,8 @@ import com.github.fge.jsonschema.processors.format.FormatProcessor; import com.google.common.base.Equivalence; +import javax.annotation.ParametersAreNonnullByDefault; + /** * A validation chain * @@ -80,8 +81,7 @@ public ValidationChain(final RefResolver refResolver, = ProcessorChain.startWith(digester).chainWith(keywordBuilder); if (cfg.getUseFormat()) { - final FormatProcessor format - = new FormatProcessor(library, cfg); + final FormatProcessor format = new FormatProcessor(library, cfg); chain2 = chain2.chainWith(format); } @@ -120,26 +120,24 @@ public String toString() return resolver + " -> " + builder; } + @ParametersAreNonnullByDefault private static final class SchemaHolderEquivalence extends Equivalence> { private static final Equivalence> INSTANCE = new SchemaHolderEquivalence(); - private static final Equivalence EQUIVALENCE - = SchemaTreeEquivalence.getInstance(); - @Override protected boolean doEquivalent(final ValueHolder a, final ValueHolder b) { - return EQUIVALENCE.equivalent(a.getValue(), b.getValue()); + return a.getValue().equals(b.getValue()); } @Override protected int doHash(final ValueHolder t) { - return EQUIVALENCE.hash(t.getValue()); + return t.getValue().hashCode(); } } } From 8ad8249fed98e8eb66d67e77f918d470e7f4edb6 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sat, 24 May 2014 14:02:50 +0200 Subject: [PATCH 24/40] Modify tests; add a message for validation loops Signed-off-by: Francis Galiegue --- .../jsonschema/validator/validation.properties | 5 +++++ .../validation/ValidationProcessorTest.java | 17 +++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/resources/com/github/fge/jsonschema/validator/validation.properties b/src/main/resources/com/github/fge/jsonschema/validator/validation.properties index 99ac7b4a7..380eb5c22 100644 --- a/src/main/resources/com/github/fge/jsonschema/validator/validation.properties +++ b/src/main/resources/com/github/fge/jsonschema/validator/validation.properties @@ -74,3 +74,8 @@ err.format.jsonpointer.invalid = input string "%s" is not a valid JSON Pointer err.format.macAddr.invalid = input string "%s" is not a valid MAC address err.format.uriTemplate.invalid = input string "%s" is not a valid URI template err.format.UUID.invalid = input string "%s" is not a valid UUID + +# +# Other messages +# +err.common.validationLoop = validation loop detected diff --git a/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java b/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java index f51123c01..5fa85df26 100644 --- a/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java +++ b/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java @@ -41,8 +41,10 @@ import com.github.fge.jsonschema.library.Library; import com.github.fge.jsonschema.main.JsonSchemaFactory; import com.github.fge.jsonschema.main.JsonValidator; +import com.github.fge.jsonschema.messages.JsonSchemaValidationBundle; import com.github.fge.jsonschema.processors.data.FullData; import com.github.fge.msgsimple.bundle.MessageBundle; +import com.github.fge.msgsimple.load.MessageBundles; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -52,6 +54,7 @@ import static org.mockito.Mockito.mock; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; public final class ValidationProcessorTest { @@ -123,8 +126,18 @@ public void circularReferencingDuringValidationIsDetected() = JsonLoader.fromResource("/other/issue102.json"); final JsonSchemaFactory factory = JsonSchemaFactory.byDefault(); final JsonValidator validator = factory.getValidator(); - - validator.validate(schemaNode, JacksonUtils.nodeFactory().nullNode()); + final MessageBundle bundle + = MessageBundles.getBundle(JsonSchemaValidationBundle.class); + final String expectedMsg + = bundle.getMessage("err.common.validationLoop"); + + try { + validator.validate(schemaNode, + JacksonUtils.nodeFactory().nullNode()); + fail("No exception thrown!"); + } catch (ProcessingException e) { + assertEquals(e.getMessage(), expectedMsg); + } assertTrue(true); } From 821b37f985361135863444acc558cc22a8b023c8 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sat, 24 May 2014 14:17:20 +0200 Subject: [PATCH 25/40] ValidationProcessor: don't cache {Array,Object}SchemaSelector results anymore Given the issue, the plan is to create a ValidationProcessor for each validation. As such, caching these doesn't make sense anymore. Signed-off-by: Francis Galiegue --- .../processors/validation/ValidationProcessor.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationProcessor.java b/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationProcessor.java index c824c14e8..a1106704f 100644 --- a/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationProcessor.java +++ b/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationProcessor.java @@ -37,9 +37,7 @@ import com.github.fge.jsonschema.processors.data.SchemaContext; import com.github.fge.jsonschema.processors.data.ValidatorList; import com.github.fge.msgsimple.bundle.MessageBundle; -import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; import com.google.common.collect.Lists; import java.util.Collections; @@ -54,8 +52,6 @@ public final class ValidationProcessor private final MessageBundle syntaxMessages; private final MessageBundle validationMessages; private final Processor processor; - private final LoadingCache arrayCache; - private final LoadingCache objectCache; public ValidationProcessor(final ValidationConfiguration cfg, final Processor processor) @@ -65,8 +61,6 @@ public ValidationProcessor(final ValidationConfiguration cfg, this.processor = new CachingProcessor( processor, SchemaContextEquivalence.getInstance() ); - arrayCache = CacheBuilder.newBuilder().build(arrayLoader()); - objectCache = CacheBuilder.newBuilder().build(objectLoader()); } @Override @@ -155,7 +149,7 @@ private void processArray(final ProcessingReport report, final JsonNode node = instance.getNode(); final JsonNode digest = ArraySchemaDigester.getInstance().digest(schema); - final ArraySchemaSelector selector = arrayCache.getUnchecked(digest); + final ArraySchemaSelector selector = new ArraySchemaSelector(digest); final int size = node.size(); @@ -184,7 +178,7 @@ private void processObject(final ProcessingReport report, final JsonNode digest = ObjectSchemaDigester.getInstance() .digest(schema); - final ObjectSchemaSelector selector = objectCache.getUnchecked(digest); + final ObjectSchemaSelector selector = new ObjectSchemaSelector(digest); final List fields = Lists.newArrayList(node.fieldNames()); Collections.sort(fields); From a2488d7ae3d45744168141afab349126e195da55 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sat, 24 May 2014 15:15:26 +0200 Subject: [PATCH 26/40] Deprecate a constructor of FullData Signed-off-by: Francis Galiegue --- .../java/com/github/fge/jsonschema/processors/data/FullData.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/github/fge/jsonschema/processors/data/FullData.java b/src/main/java/com/github/fge/jsonschema/processors/data/FullData.java index f3d6cd6d0..d30c02434 100644 --- a/src/main/java/com/github/fge/jsonschema/processors/data/FullData.java +++ b/src/main/java/com/github/fge/jsonschema/processors/data/FullData.java @@ -61,6 +61,7 @@ public FullData(final SchemaTree schema, final JsonTree instance) this(schema, instance, false); } + @Deprecated public FullData(final SchemaTree schema) { this(schema, null); From 477e7c5e2487e398ab0e43b9f9002f1a163f5bc5 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sat, 24 May 2014 16:40:40 +0200 Subject: [PATCH 27/40] New class InstanceValidator The existing ValidationProcessor is not easily modifiable to fulfill the needs highlighted by issue #102. A ThreadLocal may be used, but that would make the code rather convoluted. Instead, create a new InstanceValidator class with the same signature; the ValidationProcessor (which is unique per JsonSchemaFactory) will then create an instance of that class for each attempted schema/instance validation pair. This means the InstanceValidator instance will be what keyword validators will use, instead of the ValidationProcessor. Signed-off-by: Francis Galiegue --- .../validation/InstanceValidator.java | 246 ++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java diff --git a/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java b/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java new file mode 100644 index 000000000..d21ec2388 --- /dev/null +++ b/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2014, Francis Galiegue (fgaliegue@gmail.com) + * + * This software is dual-licensed under: + * + * - the Lesser General Public License (LGPL) version 3.0 or, at your option, any + * later version; + * - the Apache Software License (ASL) version 2.0. + * + * The text of this file and of both licenses is available at the root of this + * project or, if you have the jar distribution, in directory META-INF/, under + * the names LGPL-3.0.txt and ASL-2.0.txt respectively. + * + * Direct link to the sources: + * + * - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt + * - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package com.github.fge.jsonschema.processors.validation; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.github.fge.jackson.JacksonUtils; +import com.github.fge.jackson.jsonpointer.JsonPointer; +import com.github.fge.jsonschema.core.exceptions.InvalidSchemaException; +import com.github.fge.jsonschema.core.exceptions.ProcessingException; +import com.github.fge.jsonschema.core.processing.Processor; +import com.github.fge.jsonschema.core.report.ProcessingMessage; +import com.github.fge.jsonschema.core.report.ProcessingReport; +import com.github.fge.jsonschema.core.tree.JsonTree; +import com.github.fge.jsonschema.core.tree.SchemaTree; +import com.github.fge.jsonschema.keyword.validator.KeywordValidator; +import com.github.fge.jsonschema.processors.data.FullData; +import com.github.fge.jsonschema.processors.data.SchemaContext; +import com.github.fge.jsonschema.processors.data.ValidatorList; +import com.github.fge.msgsimple.bundle.MessageBundle; +import com.google.common.base.Equivalence; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import javax.annotation.ParametersAreNonnullByDefault; +import javax.annotation.concurrent.NotThreadSafe; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * Main validation processor + */ +@NotThreadSafe +@ParametersAreNonnullByDefault +public final class InstanceValidator + implements Processor +{ + private final MessageBundle syntaxMessages; + private final MessageBundle validationMessages; + private final Processor keywordBuilder; + + private final Set> visited + = Sets.newLinkedHashSet(); + + public InstanceValidator(final MessageBundle syntaxMessages, + final MessageBundle validationMessages, + final Processor keywordBuilder) + { + this.syntaxMessages = syntaxMessages; + this.validationMessages = validationMessages; + this.keywordBuilder = keywordBuilder; + } + + @Override + public FullData process(final ProcessingReport report, + final FullData input) + throws ProcessingException + { + if (!visited.add(FULL_DATA_EQUIVALENCE.wrap(input))) { + final String errmsg + = validationMessages.getMessage("err.common.validationLoop"); + final ProcessingMessage message = input.newMessage() + .put("domain", "validation") + .setMessage(errmsg) + .put("visited", visited); + throw new ProcessingException(message); + } + + /* + * Build a validation context, attach a report to it + */ + final SchemaContext context = new SchemaContext(input); + + /* + * Get the full context from the cache. Inject the messages into the + * main report. + */ + final ValidatorList fullContext = keywordBuilder.process(report, + context); + + if (fullContext == null) { + final ProcessingMessage message = collectSyntaxErrors(report); + throw new InvalidSchemaException(message); + } + + /* + * Get the calculated context. Build the data. + */ + final SchemaContext newContext = fullContext.getContext(); + final FullData data = new FullData(newContext.getSchema(), + input.getInstance(), input.isDeepCheck()); + + /* + * Validate against all keywords. + */ + for (final KeywordValidator validator: fullContext) + validator.validate(this, report, validationMessages, data); + + /* + * At that point, if the report is a failure, we quit: there is no + * reason to go any further. Unless the user has asked to continue even + * in this case. + */ + if (!(report.isSuccess() || data.isDeepCheck())) + return input; + + /* + * Now check whether this is a container node with a size greater than + * 0. If not, no need to go see the children. + */ + final JsonNode node = data.getInstance().getNode(); + if (node.size() == 0) + return input; + + if (node.isArray()) + processArray(report, data); + else + processObject(report, data); + + return input; + } + + private ProcessingMessage collectSyntaxErrors(final ProcessingReport report) + { + /* + * OK, that's for issue #99 but that's ugly nevertheless. + * + * We want syntax error messages to appear in the exception text. + */ + final String msg = syntaxMessages.getMessage("core.invalidSchema"); + final ArrayNode arrayNode = JacksonUtils.nodeFactory().arrayNode(); + JsonNode node; + for (final ProcessingMessage message: report) { + node = message.asJson(); + if ("syntax".equals(node.path("domain").asText())) + arrayNode.add(node); + } + final StringBuilder sb = new StringBuilder(msg); + sb.append("\nSyntax errors:\n"); + sb.append(JacksonUtils.prettyPrint(arrayNode)); + return new ProcessingMessage().setMessage(sb.toString()); + } + + private void processArray(final ProcessingReport report, + final FullData input) + throws ProcessingException + { + final SchemaTree tree = input.getSchema(); + final JsonTree instance = input.getInstance(); + + final JsonNode schema = tree.getNode(); + final JsonNode node = instance.getNode(); + + final JsonNode digest = ArraySchemaDigester.getInstance().digest(schema); + final ArraySchemaSelector selector = new ArraySchemaSelector(digest); + + final int size = node.size(); + + FullData data; + JsonTree newInstance; + + for (int index = 0; index < size; index++) { + newInstance = instance.append(JsonPointer.of(index)); + data = input.withInstance(newInstance); + for (final JsonPointer ptr: selector.selectSchemas(index)) { + data = data.withSchema(tree.append(ptr)); + process(report, data); + } + } + } + + private void processObject(final ProcessingReport report, + final FullData input) + throws ProcessingException + { + final SchemaTree tree = input.getSchema(); + final JsonTree instance = input.getInstance(); + + final JsonNode schema = tree.getNode(); + final JsonNode node = instance.getNode(); + + final JsonNode digest = ObjectSchemaDigester.getInstance() + .digest(schema); + final ObjectSchemaSelector selector = new ObjectSchemaSelector(digest); + + final List fields = Lists.newArrayList(node.fieldNames()); + Collections.sort(fields); + + FullData data; + JsonTree newInstance; + + for (final String field: fields) { + newInstance = instance.append(JsonPointer.of(field)); + data = input.withInstance(newInstance); + for (final JsonPointer ptr: selector.selectSchemas(field)) { + data = data.withSchema(tree.append(ptr)); + process(report, data); + } + } + } + + @Override + public String toString() + { + return "instance validator"; + } + + @ParametersAreNonnullByDefault + private static final Equivalence FULL_DATA_EQUIVALENCE + = new Equivalence() + { + @Override + protected boolean doEquivalent(final FullData a, final FullData b) + { + final JsonPointer ptra = a.getInstance().getPointer(); + final JsonPointer ptrb = b.getInstance().getPointer(); + return a.getSchema().equals(b.getSchema()) + && ptra.equals(ptrb); + } + + @Override + protected int doHash(final FullData t) + { + return t.getSchema().hashCode() + ^ t.getInstance().getPointer().hashCode(); + } + }; +} From e13ce00febbaa64e16210446e0d1527b5ffe36ee Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sat, 24 May 2014 17:24:29 +0200 Subject: [PATCH 28/40] ValidationProcessor: receive the cached processor directly from the factory There is no point in building it into the processor itself. Signed-off-by: Francis Galiegue --- .../com/github/fge/jsonschema/main/JsonSchemaFactory.java | 7 ++++++- .../processors/validation/ValidationProcessor.java | 4 +--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/github/fge/jsonschema/main/JsonSchemaFactory.java b/src/main/java/com/github/fge/jsonschema/main/JsonSchemaFactory.java index 2bf046c62..bfaca5fe0 100644 --- a/src/main/java/com/github/fge/jsonschema/main/JsonSchemaFactory.java +++ b/src/main/java/com/github/fge/jsonschema/main/JsonSchemaFactory.java @@ -30,6 +30,7 @@ import com.github.fge.jsonschema.core.load.SchemaLoader; import com.github.fge.jsonschema.core.load.configuration.LoadingConfiguration; import com.github.fge.jsonschema.core.messages.JsonSchemaCoreMessageBundle; +import com.github.fge.jsonschema.core.processing.CachingProcessor; import com.github.fge.jsonschema.core.processing.Processor; import com.github.fge.jsonschema.core.processing.ProcessorMap; import com.github.fge.jsonschema.core.ref.JsonRef; @@ -40,6 +41,7 @@ import com.github.fge.jsonschema.processors.data.SchemaContext; import com.github.fge.jsonschema.processors.data.ValidatorList; import com.github.fge.jsonschema.processors.syntax.SyntaxValidator; +import com.github.fge.jsonschema.processors.validation.SchemaContextEquivalence; import com.github.fge.jsonschema.processors.validation.ValidationChain; import com.github.fge.jsonschema.processors.validation.ValidationProcessor; import com.github.fge.msgsimple.bundle.MessageBundle; @@ -270,6 +272,9 @@ private Processor buildProcessor() map.addEntry(ref, chain); } - return map.getProcessor(); + final Processor processor + = map.getProcessor(); + return new CachingProcessor(processor, + SchemaContextEquivalence.getInstance()); } } diff --git a/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationProcessor.java b/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationProcessor.java index a1106704f..2dce47e52 100644 --- a/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationProcessor.java +++ b/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationProcessor.java @@ -58,9 +58,7 @@ public ValidationProcessor(final ValidationConfiguration cfg, { syntaxMessages = cfg.getSyntaxMessages(); validationMessages = cfg.getValidationMessages(); - this.processor = new CachingProcessor( - processor, SchemaContextEquivalence.getInstance() - ); + this.processor = processor; } @Override From 0a0d532cf18643cf4fca360cba58a55cde44d90b Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sat, 24 May 2014 17:25:30 +0200 Subject: [PATCH 29/40] Fix issue #102 As planned; create an instance of InstanceValidator for each schema/instance validation pair and act on that. Signed-off-by: Francis Galiegue --- .../validation/InstanceValidator.java | 15 +- .../validation/ValidationProcessor.java | 168 +----------------- .../validation/ValidationProcessorTest.java | 2 +- 3 files changed, 18 insertions(+), 167 deletions(-) diff --git a/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java b/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java index d21ec2388..118a4acbe 100644 --- a/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java +++ b/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.github.fge.jackson.JacksonUtils; import com.github.fge.jackson.jsonpointer.JsonPointer; import com.github.fge.jsonschema.core.exceptions.InvalidSchemaException; @@ -77,10 +78,14 @@ public FullData process(final ProcessingReport report, if (!visited.add(FULL_DATA_EQUIVALENCE.wrap(input))) { final String errmsg = validationMessages.getMessage("err.common.validationLoop"); + final ArrayNode node = JacksonUtils.nodeFactory().arrayNode(); + for (final Equivalence.Wrapper e: visited) + //noinspection ConstantConditions + node.add(toJson(e.get())); final ProcessingMessage message = input.newMessage() .put("domain", "validation") .setMessage(errmsg) - .put("visited", visited); + .put("visited", node); throw new ProcessingException(message); } @@ -223,6 +228,14 @@ public String toString() return "instance validator"; } + private static JsonNode toJson(final FullData data) + { + final ObjectNode node = JacksonUtils.nodeFactory().objectNode(); + node.put("schema", data.getSchema().asJson()); + node.put("instance", data.getInstance().asJson()); + return node; + } + @ParametersAreNonnullByDefault private static final Equivalence FULL_DATA_EQUIVALENCE = new Equivalence() diff --git a/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationProcessor.java b/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationProcessor.java index 2dce47e52..4a5b94923 100644 --- a/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationProcessor.java +++ b/src/main/java/com/github/fge/jsonschema/processors/validation/ValidationProcessor.java @@ -19,29 +19,14 @@ package com.github.fge.jsonschema.processors.validation; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.github.fge.jackson.JacksonUtils; -import com.github.fge.jackson.jsonpointer.JsonPointer; import com.github.fge.jsonschema.cfg.ValidationConfiguration; -import com.github.fge.jsonschema.core.exceptions.InvalidSchemaException; import com.github.fge.jsonschema.core.exceptions.ProcessingException; -import com.github.fge.jsonschema.core.processing.CachingProcessor; import com.github.fge.jsonschema.core.processing.Processor; -import com.github.fge.jsonschema.core.report.ProcessingMessage; import com.github.fge.jsonschema.core.report.ProcessingReport; -import com.github.fge.jsonschema.core.tree.JsonTree; -import com.github.fge.jsonschema.core.tree.SchemaTree; -import com.github.fge.jsonschema.keyword.validator.KeywordValidator; import com.github.fge.jsonschema.processors.data.FullData; import com.github.fge.jsonschema.processors.data.SchemaContext; import com.github.fge.jsonschema.processors.data.ValidatorList; import com.github.fge.msgsimple.bundle.MessageBundle; -import com.google.common.cache.CacheLoader; -import com.google.common.collect.Lists; - -import java.util.Collections; -import java.util.List; /** * Main validation processor @@ -66,156 +51,9 @@ public FullData process(final ProcessingReport report, final FullData input) throws ProcessingException { - /* - * Build a validation context, attach a report to it - */ - final SchemaContext context = new SchemaContext(input); - - /* - * Get the full context from the cache. Inject the messages into the - * main report. - */ - final ValidatorList fullContext = processor.process(report, context); - - if (fullContext == null) { - /* - * OK, that's for issue #99 but that's ugly nevertheless. - * - * We want syntax error messages to appear in the exception text. - */ - final String msg = syntaxMessages.getMessage("core.invalidSchema"); - final ArrayNode arrayNode = JacksonUtils.nodeFactory().arrayNode(); - JsonNode node; - for (final ProcessingMessage message: report) { - node = message.asJson(); - if ("syntax".equals(node.path("domain").asText())) - arrayNode.add(node); - } - final StringBuilder sb = new StringBuilder(msg); - sb.append("\nSyntax errors:\n"); - sb.append(JacksonUtils.prettyPrint(arrayNode)); - final ProcessingMessage message = new ProcessingMessage() - .setMessage(sb.toString()); - throw new InvalidSchemaException(message); - } - - /* - * Get the calculated context. Build the data. - */ - final SchemaContext newContext = fullContext.getContext(); - final FullData data = new FullData(newContext.getSchema(), - input.getInstance(), input.isDeepCheck()); - - /* - * Validate against all keywords. - */ - for (final KeywordValidator validator: fullContext) - validator.validate(this, report, validationMessages, data); - - /* - * At that point, if the report is a failure, we quit: there is no - * reason to go any further. Unless the user has asked to continue even - * in this case. - */ - if (!(report.isSuccess() || data.isDeepCheck())) - return input; - - /* - * Now check whether this is a container node with a size greater than - * 0. If not, no need to go see the children. - */ - final JsonNode node = data.getInstance().getNode(); - if (node.size() == 0) - return input; - - if (node.isArray()) - processArray(report, data); - else - processObject(report, data); - - return input; - } - - private void processArray(final ProcessingReport report, - final FullData input) - throws ProcessingException - { - final SchemaTree tree = input.getSchema(); - final JsonTree instance = input.getInstance(); - - final JsonNode schema = tree.getNode(); - final JsonNode node = instance.getNode(); - - final JsonNode digest = ArraySchemaDigester.getInstance().digest(schema); - final ArraySchemaSelector selector = new ArraySchemaSelector(digest); - - final int size = node.size(); - - FullData data; - JsonTree newInstance; - - for (int index = 0; index < size; index++) { - newInstance = instance.append(JsonPointer.of(index)); - data = input.withInstance(newInstance); - for (final JsonPointer ptr: selector.selectSchemas(index)) { - data = data.withSchema(tree.append(ptr)); - process(report, data); - } - } - } - - private void processObject(final ProcessingReport report, - final FullData input) - throws ProcessingException - { - final SchemaTree tree = input.getSchema(); - final JsonTree instance = input.getInstance(); - - final JsonNode schema = tree.getNode(); - final JsonNode node = instance.getNode(); - - final JsonNode digest = ObjectSchemaDigester.getInstance() - .digest(schema); - final ObjectSchemaSelector selector = new ObjectSchemaSelector(digest); - - final List fields = Lists.newArrayList(node.fieldNames()); - Collections.sort(fields); - - FullData data; - JsonTree newInstance; - - for (final String field: fields) { - newInstance = instance.append(JsonPointer.of(field)); - data = input.withInstance(newInstance); - for (final JsonPointer ptr: selector.selectSchemas(field)) { - data = data.withSchema(tree.append(ptr)); - process(report, data); - } - } - } - - private static CacheLoader arrayLoader() - { - return new CacheLoader() - { - @Override - public ArraySchemaSelector load(final JsonNode key) - { - return new ArraySchemaSelector(key); - } - }; - } - - private static CacheLoader objectLoader() - { - return new CacheLoader() - { - @Override - public ObjectSchemaSelector load(final JsonNode key) - { - return new ObjectSchemaSelector(key); - } - }; + final InstanceValidator validator = new InstanceValidator( + syntaxMessages, validationMessages, processor); + return validator.process(report, input); } @Override diff --git a/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java b/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java index 5fa85df26..d77c2e242 100644 --- a/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java +++ b/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java @@ -136,7 +136,7 @@ public void circularReferencingDuringValidationIsDetected() JacksonUtils.nodeFactory().nullNode()); fail("No exception thrown!"); } catch (ProcessingException e) { - assertEquals(e.getMessage(), expectedMsg); + assertEquals(e.getProcessingMessage().getMessage(), expectedMsg); } assertTrue(true); } From f6e684d2922f16ffc7594deb4362de479ace5a82 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sat, 24 May 2014 17:52:57 +0200 Subject: [PATCH 30/40] Improve error message Signed-off-by: Francis Galiegue --- .../validation/InstanceValidator.java | 19 ++++++++++++++----- .../validator/validation.properties | 3 ++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java b/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java index 118a4acbe..d269ad31a 100644 --- a/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java +++ b/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java @@ -21,7 +21,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.github.fge.jackson.JacksonUtils; import com.github.fge.jackson.jsonpointer.JsonPointer; import com.github.fge.jsonschema.core.exceptions.InvalidSchemaException; @@ -36,12 +35,16 @@ import com.github.fge.jsonschema.processors.data.SchemaContext; import com.github.fge.jsonschema.processors.data.ValidatorList; import com.github.fge.msgsimple.bundle.MessageBundle; +import com.github.fge.uritemplate.URITemplate; +import com.github.fge.uritemplate.URITemplateException; +import com.github.fge.uritemplate.vars.VariableMap; import com.google.common.base.Equivalence; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.concurrent.NotThreadSafe; +import java.net.URI; import java.util.Collections; import java.util.List; import java.util.Set; @@ -230,10 +233,16 @@ public String toString() private static JsonNode toJson(final FullData data) { - final ObjectNode node = JacksonUtils.nodeFactory().objectNode(); - node.put("schema", data.getSchema().asJson()); - node.put("instance", data.getInstance().asJson()); - return node; + final SchemaTree tree = data.getSchema(); + final URI baseUri = tree.getLoadingRef().getLocator(); + try { + final URITemplate template = new URITemplate(baseUri + "{+ptr}"); + final VariableMap vars = VariableMap.newBuilder().addScalarValue( + "ptr", tree.getPointer()).freeze(); + return JacksonUtils.nodeFactory().textNode(template.toString(vars)); + } catch (URITemplateException e) { + throw new IllegalStateException("wtf??", e); + } } @ParametersAreNonnullByDefault diff --git a/src/main/resources/com/github/fge/jsonschema/validator/validation.properties b/src/main/resources/com/github/fge/jsonschema/validator/validation.properties index 380eb5c22..9f82f4576 100644 --- a/src/main/resources/com/github/fge/jsonschema/validator/validation.properties +++ b/src/main/resources/com/github/fge/jsonschema/validator/validation.properties @@ -78,4 +78,5 @@ err.format.UUID.invalid = input string "%s" is not a valid UUID # # Other messages # -err.common.validationLoop = validation loop detected +err.common.validationLoop = validation loop: same schema visited more than \ + once for the same instance From f12a8e38b7226007864ee59f5aea11517e3a1de2 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sat, 24 May 2014 19:45:34 +0200 Subject: [PATCH 31/40] InstanceValidator: improve test, documentation, error message Also rewrite a schema for a more simple example Signed-off-by: Francis Galiegue --- .../validation/InstanceValidator.java | 30 ++++++++++++++++-- .../validator/validation.properties | 3 +- .../validation/ValidationProcessorTest.java | 21 ++++++++++--- src/test/resources/other/issue102.json | 31 +------------------ 4 files changed, 46 insertions(+), 39 deletions(-) diff --git a/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java b/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java index d269ad31a..bc062c719 100644 --- a/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java +++ b/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java @@ -61,6 +61,26 @@ public final class InstanceValidator private final MessageBundle validationMessages; private final Processor keywordBuilder; + /* + * It is possible to trigger a validation loop if there is a repeated + * triplet schema ID/schema pointer/instance pointer while we validate; + * example schema: + * + * { "oneOf": [ {}, { "$ref": "#" } ] } + * + * Whatever the data, validation will end up validating, for a same pointer + * into the instance, the following pointers into the schema: + * + * "" -> "/oneOf/0" -> "/oneOf/1" -> "" <-- LOOP + * + * This is not a JSON Reference loop here, but truly a validation loop. + * + * We therefore use this set to record the triplets seen by using an + * Equivalence over FullData which detects this. This is helped by the fact + * that SchemaTree now implements equals()/hashCode() in -core; since this + * class is instantiated for each instance validation, we are certain that + * what instance pointer is seen is the one of the instance we validate. + */ private final Set> visited = Sets.newLinkedHashSet(); @@ -88,7 +108,10 @@ public FullData process(final ProcessingReport report, final ProcessingMessage message = input.newMessage() .put("domain", "validation") .setMessage(errmsg) - .put("visited", node); + .putArgument("alreadyVisited", toJson(input)) + .putArgument("instancePointer", + input.getInstance().getPointer().toString()) + .put("validationPath", node); throw new ProcessingException(message); } @@ -231,15 +254,16 @@ public String toString() return "instance validator"; } - private static JsonNode toJson(final FullData data) + private static String toJson(final FullData data) { final SchemaTree tree = data.getSchema(); final URI baseUri = tree.getLoadingRef().getLocator(); try { + // TODO: there should be an easier way to do that... final URITemplate template = new URITemplate(baseUri + "{+ptr}"); final VariableMap vars = VariableMap.newBuilder().addScalarValue( "ptr", tree.getPointer()).freeze(); - return JacksonUtils.nodeFactory().textNode(template.toString(vars)); + return template.toString(vars); } catch (URITemplateException e) { throw new IllegalStateException("wtf??", e); } diff --git a/src/main/resources/com/github/fge/jsonschema/validator/validation.properties b/src/main/resources/com/github/fge/jsonschema/validator/validation.properties index 9f82f4576..c3bff310f 100644 --- a/src/main/resources/com/github/fge/jsonschema/validator/validation.properties +++ b/src/main/resources/com/github/fge/jsonschema/validator/validation.properties @@ -78,5 +78,4 @@ err.format.UUID.invalid = input string "%s" is not a valid UUID # # Other messages # -err.common.validationLoop = validation loop: same schema visited more than \ - once for the same instance +err.common.validationLoop = validation loop: schema "%s" visited twice for pointer "%s" into validated instance diff --git a/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java b/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java index d77c2e242..d72b35323 100644 --- a/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java +++ b/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java @@ -26,10 +26,13 @@ import com.github.fge.jackson.JacksonUtils; import com.github.fge.jackson.JsonLoader; import com.github.fge.jackson.NodeType; +import com.github.fge.jackson.jsonpointer.JsonPointer; +import com.github.fge.jackson.jsonpointer.JsonPointerException; import com.github.fge.jsonschema.cfg.ValidationConfiguration; import com.github.fge.jsonschema.core.exceptions.ProcessingException; import com.github.fge.jsonschema.core.keyword.syntax.checkers.SyntaxChecker; import com.github.fge.jsonschema.core.processing.Processor; +import com.github.fge.jsonschema.core.report.ProcessingMessage; import com.github.fge.jsonschema.core.report.ProcessingReport; import com.github.fge.jsonschema.core.tree.CanonicalSchemaTree; import com.github.fge.jsonschema.core.tree.JsonTree; @@ -49,8 +52,11 @@ import org.testng.annotations.Test; import java.io.IOException; +import java.net.URI; +import java.util.Arrays; import java.util.concurrent.atomic.AtomicInteger; +import static com.github.fge.jsonschema.matchers.ProcessingMessageAssert.assertMessage; import static org.mockito.Mockito.mock; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; @@ -120,7 +126,7 @@ public void childrenAreExploredOnDemandEvenIfContainerFails() @Test(timeOut = 1000) public void circularReferencingDuringValidationIsDetected() - throws IOException, ProcessingException + throws IOException, ProcessingException, JsonPointerException { final JsonNode schemaNode = JsonLoader.fromResource("/other/issue102.json"); @@ -128,15 +134,22 @@ public void circularReferencingDuringValidationIsDetected() final JsonValidator validator = factory.getValidator(); final MessageBundle bundle = MessageBundles.getBundle(JsonSchemaValidationBundle.class); - final String expectedMsg - = bundle.getMessage("err.common.validationLoop"); try { validator.validate(schemaNode, JacksonUtils.nodeFactory().nullNode()); fail("No exception thrown!"); } catch (ProcessingException e) { - assertEquals(e.getProcessingMessage().getMessage(), expectedMsg); + final URI uri = URI.create("#/oneOf/0"); + final ProcessingMessage message = e.getProcessingMessage(); + final String expectedMessage + = bundle.printf("err.common.validationLoop", uri, ""); + assertMessage(message) + .hasMessage(expectedMessage) + .hasField("alreadyVisited", uri) + .hasField("instancePointer", JsonPointer.empty().toString()) + .hasField("validationPath", + Arrays.asList("#", "#/oneOf/0", "#/oneOf/1")); } assertTrue(true); } diff --git a/src/test/resources/other/issue102.json b/src/test/resources/other/issue102.json index 1af9832c3..1144e4a1e 100644 --- a/src/test/resources/other/issue102.json +++ b/src/test/resources/other/issue102.json @@ -1,30 +1 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "definitions": { - "S": { - "allOf": [ - { - "type": "string", - "enum": ["a"] - }, - { - "oneOf": [ - { - "$ref": "#/definitions/S" - }, - { - "type": "string", - "enum": [""] - } - ] - }, - { - "type": "string", - "enum": ["b"] - } - ], - "additionalItems": false - } - }, - "$ref": "#/definitions/S" -} \ No newline at end of file +{ "oneOf": [ {}, { "$ref": "#" } ] } \ No newline at end of file From 15ce0e82ce916a0197f71c635e23c6378a464f0d Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sat, 24 May 2014 20:15:19 +0200 Subject: [PATCH 32/40] More documentation Signed-off-by: Francis Galiegue --- .../validation/InstanceValidator.java | 120 +++++++++++------- .../validation/ObjectSchemaDigester.java | 2 +- 2 files changed, 77 insertions(+), 45 deletions(-) diff --git a/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java b/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java index bc062c719..81ef71750 100644 --- a/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java +++ b/src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java @@ -31,12 +31,15 @@ import com.github.fge.jsonschema.core.tree.JsonTree; import com.github.fge.jsonschema.core.tree.SchemaTree; import com.github.fge.jsonschema.keyword.validator.KeywordValidator; +import com.github.fge.jsonschema.main.JsonSchema; +import com.github.fge.jsonschema.main.JsonValidator; import com.github.fge.jsonschema.processors.data.FullData; import com.github.fge.jsonschema.processors.data.SchemaContext; import com.github.fge.jsonschema.processors.data.ValidatorList; import com.github.fge.msgsimple.bundle.MessageBundle; import com.github.fge.uritemplate.URITemplate; import com.github.fge.uritemplate.URITemplateException; +import com.github.fge.uritemplate.URITemplateParseException; import com.github.fge.uritemplate.vars.VariableMap; import com.google.common.base.Equivalence; import com.google.common.collect.Lists; @@ -50,7 +53,16 @@ import java.util.Set; /** - * Main validation processor + * Processor for validating one schema/instance pair + * + *

    One such processor is created for each schema/instance validation.

    + * + *

    Internally, all validation operations provided by the API (whether that + * be a {@link JsonSchema}, via {@link JsonValidator} or using {@link + * ValidationProcessor} directly) will eventually instantiate one of these. More + * precisely, this is instantiated by {@link + * ValidationProcessor#process(ProcessingReport, FullData)}.

    + * */ @NotThreadSafe @ParametersAreNonnullByDefault @@ -71,7 +83,7 @@ public final class InstanceValidator * Whatever the data, validation will end up validating, for a same pointer * into the instance, the following pointers into the schema: * - * "" -> "/oneOf/0" -> "/oneOf/1" -> "" <-- LOOP + * "" -> "/oneOf/0" -> "/oneOf/1" -> "/oneOf/0" <-- LOOP * * This is not a JSON Reference loop here, but truly a validation loop. * @@ -84,6 +96,13 @@ public final class InstanceValidator private final Set> visited = Sets.newLinkedHashSet(); + /** + * Constructor -- do not use directly! + * + * @param syntaxMessages the syntax message bundle + * @param validationMessages the validation message bundle + * @param keywordBuilder the keyword builder + */ public InstanceValidator(final MessageBundle syntaxMessages, final MessageBundle validationMessages, final Processor keywordBuilder) @@ -98,22 +117,12 @@ public FullData process(final ProcessingReport report, final FullData input) throws ProcessingException { - if (!visited.add(FULL_DATA_EQUIVALENCE.wrap(input))) { - final String errmsg - = validationMessages.getMessage("err.common.validationLoop"); - final ArrayNode node = JacksonUtils.nodeFactory().arrayNode(); - for (final Equivalence.Wrapper e: visited) - //noinspection ConstantConditions - node.add(toJson(e.get())); - final ProcessingMessage message = input.newMessage() - .put("domain", "validation") - .setMessage(errmsg) - .putArgument("alreadyVisited", toJson(input)) - .putArgument("instancePointer", - input.getInstance().getPointer().toString()) - .put("validationPath", node); - throw new ProcessingException(message); - } + /* + * We don't want the same validation context to appear twice, see above + */ + if (!visited.add(FULL_DATA_EQUIVALENCE.wrap(input))) + throw new ProcessingException(validationLoopMessage(input)); + /* * Build a validation context, attach a report to it @@ -169,25 +178,10 @@ public FullData process(final ProcessingReport report, return input; } - private ProcessingMessage collectSyntaxErrors(final ProcessingReport report) + @Override + public String toString() { - /* - * OK, that's for issue #99 but that's ugly nevertheless. - * - * We want syntax error messages to appear in the exception text. - */ - final String msg = syntaxMessages.getMessage("core.invalidSchema"); - final ArrayNode arrayNode = JacksonUtils.nodeFactory().arrayNode(); - JsonNode node; - for (final ProcessingMessage message: report) { - node = message.asJson(); - if ("syntax".equals(node.path("domain").asText())) - arrayNode.add(node); - } - final StringBuilder sb = new StringBuilder(msg); - sb.append("\nSyntax errors:\n"); - sb.append(JacksonUtils.prettyPrint(arrayNode)); - return new ProcessingMessage().setMessage(sb.toString()); + return "instance validator"; } private void processArray(final ProcessingReport report, @@ -200,7 +194,8 @@ private void processArray(final ProcessingReport report, final JsonNode schema = tree.getNode(); final JsonNode node = instance.getNode(); - final JsonNode digest = ArraySchemaDigester.getInstance().digest(schema); + final JsonNode digest = ArraySchemaDigester.getInstance() + .digest(schema); final ArraySchemaSelector selector = new ArraySchemaSelector(digest); final int size = node.size(); @@ -248,21 +243,58 @@ private void processObject(final ProcessingReport report, } } - @Override - public String toString() + private ProcessingMessage validationLoopMessage(final FullData input) { - return "instance validator"; + final String errmsg + = validationMessages.getMessage("err.common.validationLoop"); + final ArrayNode node = JacksonUtils.nodeFactory().arrayNode(); + for (final Equivalence.Wrapper e: visited) + //noinspection ConstantConditions + node.add(toJson(e.get())); + return input.newMessage() + .put("domain", "validation") + .setMessage(errmsg) + .putArgument("alreadyVisited", toJson(input)) + .putArgument("instancePointer", + input.getInstance().getPointer().toString()) + .put("validationPath", node); + } + + private ProcessingMessage collectSyntaxErrors(final ProcessingReport report) + { + /* + * OK, that's for issue #99 but that's ugly nevertheless. + * + * We want syntax error messages to appear in the exception text. + */ + final String msg = syntaxMessages.getMessage("core.invalidSchema"); + final ArrayNode arrayNode = JacksonUtils.nodeFactory().arrayNode(); + JsonNode node; + for (final ProcessingMessage message: report) { + node = message.asJson(); + if ("syntax".equals(node.path("domain").asText())) + arrayNode.add(node); + } + final StringBuilder sb = new StringBuilder(msg); + sb.append("\nSyntax errors:\n"); + sb.append(JacksonUtils.prettyPrint(arrayNode)); + return new ProcessingMessage().setMessage(sb.toString()); } private static String toJson(final FullData data) { final SchemaTree tree = data.getSchema(); final URI baseUri = tree.getLoadingRef().getLocator(); + final VariableMap vars = VariableMap.newBuilder().addScalarValue("ptr", + tree.getPointer()).freeze(); + // TODO: there should be an easier way to do that... + final URITemplate template; + try { + template = new URITemplate(baseUri + "{+ptr}"); + } catch (URITemplateParseException e) { + throw new IllegalStateException("wtf??", e); + } try { - // TODO: there should be an easier way to do that... - final URITemplate template = new URITemplate(baseUri + "{+ptr}"); - final VariableMap vars = VariableMap.newBuilder().addScalarValue( - "ptr", tree.getPointer()).freeze(); return template.toString(vars); } catch (URITemplateException e) { throw new IllegalStateException("wtf??", e); diff --git a/src/main/java/com/github/fge/jsonschema/processors/validation/ObjectSchemaDigester.java b/src/main/java/com/github/fge/jsonschema/processors/validation/ObjectSchemaDigester.java index 98891de76..722ea8ac7 100644 --- a/src/main/java/com/github/fge/jsonschema/processors/validation/ObjectSchemaDigester.java +++ b/src/main/java/com/github/fge/jsonschema/processors/validation/ObjectSchemaDigester.java @@ -31,7 +31,7 @@ import java.util.Set; /** - * JSON Schema digester for an {@link ObjectSchemaDigester} + * JSON Schema digester for an {@link ObjectSchemaSelector} */ public final class ObjectSchemaDigester extends AbstractDigester From 96c27087f07d82de47db639b048113374599e502 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sun, 25 May 2014 00:23:04 +0200 Subject: [PATCH 33/40] One more message change Signed-off-by: Francis Galiegue --- .../com/github/fge/jsonschema/validator/validation.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/resources/com/github/fge/jsonschema/validator/validation.properties b/src/main/resources/com/github/fge/jsonschema/validator/validation.properties index c3bff310f..b26f47504 100644 --- a/src/main/resources/com/github/fge/jsonschema/validator/validation.properties +++ b/src/main/resources/com/github/fge/jsonschema/validator/validation.properties @@ -78,4 +78,5 @@ err.format.UUID.invalid = input string "%s" is not a valid UUID # # Other messages # -err.common.validationLoop = validation loop: schema "%s" visited twice for pointer "%s" into validated instance +err.common.validationLoop = validation loop: schema "%s" visited twice for \ + pointer "%s" of validated instance From e7674fa56983400acf8ccae2c19a2b464a874d74 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sun, 25 May 2014 00:58:52 +0200 Subject: [PATCH 34/40] Depend on -core 1.2.4 Essential to solve bug #102 Signed-off-by: Francis Galiegue --- build.gradle | 1 - project.gradle | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 5d67bc776..5f9fbc12c 100644 --- a/build.gradle +++ b/build.gradle @@ -72,7 +72,6 @@ apply(from: "project.gradle"); */ repositories { mavenCentral(); - mavenLocal(); } /* diff --git a/project.gradle b/project.gradle index 180ed8352..345ca5840 100644 --- a/project.gradle +++ b/project.gradle @@ -34,7 +34,7 @@ project.ext { */ dependencies { compile(group: "com.github.fge", name: "json-schema-core", - version: "1.2.4-SNAPSHOT"); + version: "1.2.4"); compile(group: "javax.mail", name: "mailapi", version: "1.4.3"); compile(group: "joda-time", name: "joda-time", version: "2.3"); compile(group: "com.googlecode.libphonenumber", name: "libphonenumber", From b21b0cb0fcad3142bb26081bee6b4a1e3e21ded3 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sun, 25 May 2014 01:22:54 +0200 Subject: [PATCH 35/40] Remove obsolete API usages Signed-off-by: Francis Galiegue --- .../fge/jsonschema/processors/syntax/SyntaxValidator.java | 3 ++- .../fge/jsonschema/format/AbstractFormatAttributeTest.java | 5 +++-- .../keyword/validator/AbstractKeywordValidatorTest.java | 4 +++- .../keyword/validator/callback/CallbackValidatorTest.java | 4 +++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/github/fge/jsonschema/processors/syntax/SyntaxValidator.java b/src/main/java/com/github/fge/jsonschema/processors/syntax/SyntaxValidator.java index 784b6238a..d3a4100df 100644 --- a/src/main/java/com/github/fge/jsonschema/processors/syntax/SyntaxValidator.java +++ b/src/main/java/com/github/fge/jsonschema/processors/syntax/SyntaxValidator.java @@ -32,6 +32,7 @@ import com.github.fge.jsonschema.core.report.ProcessingReport; import com.github.fge.jsonschema.core.tree.CanonicalSchemaTree; import com.github.fge.jsonschema.core.tree.SchemaTree; +import com.github.fge.jsonschema.core.tree.key.SchemaKey; import com.github.fge.jsonschema.core.util.Dictionary; import com.github.fge.jsonschema.core.util.ValueHolder; import com.github.fge.jsonschema.library.Library; @@ -147,6 +148,6 @@ private ProcessingResult> getResult(final JsonNode schem private static ValueHolder holder(final JsonNode node) { return ValueHolder.hold("schema", - new CanonicalSchemaTree(node)); + new CanonicalSchemaTree(SchemaKey.anonymousKey(), node)); } } diff --git a/src/test/java/com/github/fge/jsonschema/format/AbstractFormatAttributeTest.java b/src/test/java/com/github/fge/jsonschema/format/AbstractFormatAttributeTest.java index 326e44b80..c02222d35 100644 --- a/src/test/java/com/github/fge/jsonschema/format/AbstractFormatAttributeTest.java +++ b/src/test/java/com/github/fge/jsonschema/format/AbstractFormatAttributeTest.java @@ -31,6 +31,7 @@ import com.github.fge.jsonschema.core.tree.JsonTree; import com.github.fge.jsonschema.core.tree.SchemaTree; import com.github.fge.jsonschema.core.tree.SimpleJsonTree; +import com.github.fge.jsonschema.core.tree.key.SchemaKey; import com.github.fge.jsonschema.core.util.Dictionary; import com.github.fge.jsonschema.messages.JsonSchemaValidationBundle; import com.github.fge.jsonschema.processors.data.FullData; @@ -55,8 +56,8 @@ public abstract class AbstractFormatAttributeTest { protected static final MessageBundle BUNDLE = MessageBundles.getBundle(JsonSchemaValidationBundle.class); - protected static final SchemaTree SCHEMA_TREE - = new CanonicalSchemaTree(JacksonUtils.nodeFactory().objectNode()); + protected static final SchemaTree SCHEMA_TREE = new CanonicalSchemaTree( + SchemaKey.anonymousKey(), JacksonUtils.nodeFactory().objectNode()); protected final FormatAttribute attribute; protected final String fmt; diff --git a/src/test/java/com/github/fge/jsonschema/keyword/validator/AbstractKeywordValidatorTest.java b/src/test/java/com/github/fge/jsonschema/keyword/validator/AbstractKeywordValidatorTest.java index c5e8a096a..aa69bcec9 100644 --- a/src/test/java/com/github/fge/jsonschema/keyword/validator/AbstractKeywordValidatorTest.java +++ b/src/test/java/com/github/fge/jsonschema/keyword/validator/AbstractKeywordValidatorTest.java @@ -31,6 +31,7 @@ import com.github.fge.jsonschema.core.tree.JsonTree; import com.github.fge.jsonschema.core.tree.SchemaTree; import com.github.fge.jsonschema.core.tree.SimpleJsonTree; +import com.github.fge.jsonschema.core.tree.key.SchemaKey; import com.github.fge.jsonschema.core.util.Dictionary; import com.github.fge.jsonschema.messages.JsonSchemaValidationBundle; import com.github.fge.jsonschema.processors.data.FullData; @@ -110,7 +111,8 @@ public final void instancesAreValidatedCorrectly(final JsonNode digest, InstantiationException, ProcessingException { // FIXME: dummy, but we have no choice - final SchemaTree tree = new CanonicalSchemaTree(digest); + final SchemaTree tree + = new CanonicalSchemaTree(SchemaKey.anonymousKey(), digest); final JsonTree instance = new SimpleJsonTree(node); final FullData data = new FullData(tree, instance); diff --git a/src/test/java/com/github/fge/jsonschema/keyword/validator/callback/CallbackValidatorTest.java b/src/test/java/com/github/fge/jsonschema/keyword/validator/callback/CallbackValidatorTest.java index b95c35d1c..07ba21ec8 100644 --- a/src/test/java/com/github/fge/jsonschema/keyword/validator/callback/CallbackValidatorTest.java +++ b/src/test/java/com/github/fge/jsonschema/keyword/validator/callback/CallbackValidatorTest.java @@ -33,6 +33,7 @@ import com.github.fge.jsonschema.core.tree.JsonTree; import com.github.fge.jsonschema.core.tree.SchemaTree; import com.github.fge.jsonschema.core.tree.SimpleJsonTree; +import com.github.fge.jsonschema.core.tree.key.SchemaKey; import com.github.fge.jsonschema.core.util.Dictionary; import com.github.fge.jsonschema.keyword.validator.KeywordValidator; import com.github.fge.jsonschema.messages.JsonSchemaValidationBundle; @@ -88,7 +89,8 @@ protected final void initEnvironment() if (constructor == null) return; - final SchemaTree tree = new CanonicalSchemaTree(generateSchema()); + final SchemaTree tree = new CanonicalSchemaTree( + SchemaKey.anonymousKey(), generateSchema()); final JsonTree instance = new SimpleJsonTree(generateInstance()); data = new FullData(tree, instance); report = mock(ProcessingReport.class); From a3ff02b542a179d412d491a66e80796c8f27bc77 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sun, 25 May 2014 01:34:38 +0200 Subject: [PATCH 36/40] Remove deprecated API usage Signed-off-by: Francis Galiegue --- .../keyword/special/ExtendsKeywordTest.java | 4 +++- .../keyword/special/NotKeywordTest.java | 4 +++- .../keyword/special/PatternKeywordTest.java | 4 +++- .../processors/digest/SchemaDigesterTest.java | 7 +++++-- .../processors/format/FormatProcessorTest.java | 16 +++++++++++----- .../validation/ValidationProcessorTest.java | 7 +++++-- 6 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/test/java/com/github/fge/jsonschema/keyword/special/ExtendsKeywordTest.java b/src/test/java/com/github/fge/jsonschema/keyword/special/ExtendsKeywordTest.java index da1e7b3a1..36bcee0c9 100644 --- a/src/test/java/com/github/fge/jsonschema/keyword/special/ExtendsKeywordTest.java +++ b/src/test/java/com/github/fge/jsonschema/keyword/special/ExtendsKeywordTest.java @@ -31,6 +31,7 @@ import com.github.fge.jsonschema.core.tree.JsonTree; import com.github.fge.jsonschema.core.tree.SchemaTree; import com.github.fge.jsonschema.core.tree.SimpleJsonTree; +import com.github.fge.jsonschema.core.tree.key.SchemaKey; import com.github.fge.jsonschema.keyword.validator.KeywordValidator; import com.github.fge.jsonschema.library.validator.DraftV3ValidatorDictionary; import com.github.fge.jsonschema.messages.JsonSchemaValidationBundle; @@ -81,7 +82,8 @@ public void initEnvironment() final ObjectNode schema = FACTORY.objectNode(); schema.put("extends", FACTORY.objectNode()); - final SchemaTree tree = new CanonicalSchemaTree(schema); + final SchemaTree tree + = new CanonicalSchemaTree(SchemaKey.anonymousKey(), schema); final JsonTree instance = new SimpleJsonTree(FACTORY.nullNode()); data = new FullData(tree, instance); diff --git a/src/test/java/com/github/fge/jsonschema/keyword/special/NotKeywordTest.java b/src/test/java/com/github/fge/jsonschema/keyword/special/NotKeywordTest.java index 54d3de0b3..3270529a3 100644 --- a/src/test/java/com/github/fge/jsonschema/keyword/special/NotKeywordTest.java +++ b/src/test/java/com/github/fge/jsonschema/keyword/special/NotKeywordTest.java @@ -32,6 +32,7 @@ import com.github.fge.jsonschema.core.tree.JsonTree; import com.github.fge.jsonschema.core.tree.SchemaTree; import com.github.fge.jsonschema.core.tree.SimpleJsonTree; +import com.github.fge.jsonschema.core.tree.key.SchemaKey; import com.github.fge.jsonschema.keyword.validator.KeywordValidator; import com.github.fge.jsonschema.library.validator.DraftV4ValidatorDictionary; import com.github.fge.jsonschema.messages.JsonSchemaValidationBundle; @@ -82,7 +83,8 @@ public void initEnvironment() final ObjectNode schema = FACTORY.objectNode(); schema.put("not", FACTORY.objectNode()); - final SchemaTree tree = new CanonicalSchemaTree(schema); + final SchemaTree tree + = new CanonicalSchemaTree(SchemaKey.anonymousKey(), schema); final JsonTree instance = new SimpleJsonTree(FACTORY.nullNode()); data = new FullData(tree, instance); report = mock(ProcessingReport.class); diff --git a/src/test/java/com/github/fge/jsonschema/keyword/special/PatternKeywordTest.java b/src/test/java/com/github/fge/jsonschema/keyword/special/PatternKeywordTest.java index 2f0bf17e3..6ff655ae0 100644 --- a/src/test/java/com/github/fge/jsonschema/keyword/special/PatternKeywordTest.java +++ b/src/test/java/com/github/fge/jsonschema/keyword/special/PatternKeywordTest.java @@ -31,6 +31,7 @@ import com.github.fge.jsonschema.core.tree.JsonTree; import com.github.fge.jsonschema.core.tree.SchemaTree; import com.github.fge.jsonschema.core.tree.SimpleJsonTree; +import com.github.fge.jsonschema.core.tree.key.SchemaKey; import com.github.fge.jsonschema.keyword.validator.KeywordValidator; import com.github.fge.jsonschema.library.validator.CommonValidatorDictionary; import com.github.fge.jsonschema.messages.JsonSchemaValidationBundle; @@ -108,7 +109,8 @@ public void instancesAreValidatedCorrectly(final JsonNode schema, throws IllegalAccessException, InvocationTargetException, InstantiationException, ProcessingException { - final SchemaTree tree = new CanonicalSchemaTree(schema); + final SchemaTree tree + = new CanonicalSchemaTree(SchemaKey.anonymousKey(), schema); final JsonTree instance = new SimpleJsonTree(node); final FullData data = new FullData(tree, instance); diff --git a/src/test/java/com/github/fge/jsonschema/processors/digest/SchemaDigesterTest.java b/src/test/java/com/github/fge/jsonschema/processors/digest/SchemaDigesterTest.java index beed7b881..6a1866caa 100644 --- a/src/test/java/com/github/fge/jsonschema/processors/digest/SchemaDigesterTest.java +++ b/src/test/java/com/github/fge/jsonschema/processors/digest/SchemaDigesterTest.java @@ -29,6 +29,7 @@ import com.github.fge.jsonschema.core.report.ProcessingReport; import com.github.fge.jsonschema.core.tree.CanonicalSchemaTree; import com.github.fge.jsonschema.core.tree.SchemaTree; +import com.github.fge.jsonschema.core.tree.key.SchemaKey; import com.github.fge.jsonschema.core.util.Dictionary; import com.github.fge.jsonschema.core.util.DictionaryBuilder; import com.github.fge.jsonschema.keyword.digest.Digester; @@ -100,7 +101,8 @@ public void onlyRelevantDigestsAreBuilt(final JsonNode node) throws ProcessingException { final NodeType type = NodeType.getNodeType(node); - final SchemaTree tree = new CanonicalSchemaTree(schema); + final SchemaTree tree + = new CanonicalSchemaTree(SchemaKey.anonymousKey(), schema); final SchemaContext context = new SchemaContext(tree, type); final ProcessingReport report = mock(ProcessingReport.class); @@ -129,7 +131,8 @@ public void nonPresentKeywordDoesNotTriggerBuild() { final ObjectNode node = FACTORY.objectNode(); node.put(K1, K1); - final SchemaTree schemaTree = new CanonicalSchemaTree(node); + final SchemaTree schemaTree + = new CanonicalSchemaTree(SchemaKey.anonymousKey(), node); final SchemaContext context = new SchemaContext(schemaTree, NodeType.NULL); final ProcessingReport report = mock(ProcessingReport.class); diff --git a/src/test/java/com/github/fge/jsonschema/processors/format/FormatProcessorTest.java b/src/test/java/com/github/fge/jsonschema/processors/format/FormatProcessorTest.java index 5dd4b621d..3d40527e4 100644 --- a/src/test/java/com/github/fge/jsonschema/processors/format/FormatProcessorTest.java +++ b/src/test/java/com/github/fge/jsonschema/processors/format/FormatProcessorTest.java @@ -33,6 +33,7 @@ import com.github.fge.jsonschema.core.tree.JsonTree; import com.github.fge.jsonschema.core.tree.SchemaTree; import com.github.fge.jsonschema.core.tree.SimpleJsonTree; +import com.github.fge.jsonschema.core.tree.key.SchemaKey; import com.github.fge.jsonschema.core.util.Dictionary; import com.github.fge.jsonschema.format.FormatAttribute; import com.github.fge.jsonschema.keyword.validator.KeywordValidator; @@ -87,7 +88,8 @@ public void noFormatInSchemaIsANoOp() throws ProcessingException { final ObjectNode schema = FACTORY.objectNode(); - final SchemaTree tree = new CanonicalSchemaTree(schema); + final SchemaTree tree + = new CanonicalSchemaTree(SchemaKey.anonymousKey(), schema); final SchemaContext context = new SchemaContext(tree, NodeType.NULL); final ValidatorList in = new ValidatorList(context, Collections.emptyList()); @@ -105,7 +107,8 @@ public void unknownFormatAttributesAreReportedAsWarnings() { final ObjectNode schema = FACTORY.objectNode(); schema.put("format", "foo"); - final SchemaTree tree = new CanonicalSchemaTree(schema); + final SchemaTree tree + = new CanonicalSchemaTree(SchemaKey.anonymousKey(), schema); final SchemaContext context = new SchemaContext(tree, NodeType.NULL); final ValidatorList in = new ValidatorList(context, Collections.emptyList()); @@ -133,7 +136,8 @@ public void attributeIsBeingAskedWhatIsSupports() { final ObjectNode schema = FACTORY.objectNode(); schema.put("format", FMT); - final SchemaTree tree = new CanonicalSchemaTree(schema); + final SchemaTree tree + = new CanonicalSchemaTree(SchemaKey.anonymousKey(), schema); final SchemaContext context = new SchemaContext(tree, NodeType.NULL); final ValidatorList in = new ValidatorList(context, Collections.emptyList()); @@ -157,7 +161,8 @@ public void supportedNodeTypesTriggerAttributeBuild(final JsonNode node) { final ObjectNode schema = FACTORY.objectNode(); schema.put("format", FMT); - final SchemaTree tree = new CanonicalSchemaTree(schema); + final SchemaTree tree + = new CanonicalSchemaTree(SchemaKey.anonymousKey(), schema); final JsonTree instance = new SimpleJsonTree(node); final FullData data = new FullData(tree, instance); final SchemaContext context = new SchemaContext(data); @@ -192,7 +197,8 @@ public void unsupportedTypeDoesNotTriggerValidatorBuild(final JsonNode node) { final ObjectNode schema = FACTORY.objectNode(); schema.put("format", FMT); - final SchemaTree tree = new CanonicalSchemaTree(schema); + final SchemaTree tree + = new CanonicalSchemaTree(SchemaKey.anonymousKey(), schema); final SchemaContext context = new SchemaContext(tree, NodeType.getNodeType(node)); final ValidatorList in = new ValidatorList(context, diff --git a/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java b/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java index d72b35323..41d828eda 100644 --- a/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java +++ b/src/test/java/com/github/fge/jsonschema/processors/validation/ValidationProcessorTest.java @@ -38,6 +38,7 @@ import com.github.fge.jsonschema.core.tree.JsonTree; import com.github.fge.jsonschema.core.tree.SchemaTree; import com.github.fge.jsonschema.core.tree.SimpleJsonTree; +import com.github.fge.jsonschema.core.tree.key.SchemaKey; import com.github.fge.jsonschema.keyword.validator.AbstractKeywordValidator; import com.github.fge.jsonschema.library.DraftV4Library; import com.github.fge.jsonschema.library.Keyword; @@ -104,7 +105,8 @@ public void init() public void childrenAreNotExploredByDefaultIfContainerFails() throws ProcessingException { - final SchemaTree schema = new CanonicalSchemaTree(RAWSCHEMA); + final SchemaTree schema + = new CanonicalSchemaTree(SchemaKey.anonymousKey(), RAWSCHEMA); final JsonTree instance = new SimpleJsonTree(RAWINSTANCE); final FullData data = new FullData(schema, instance); final ProcessingReport report = mock(ProcessingReport.class); @@ -116,7 +118,8 @@ public void childrenAreNotExploredByDefaultIfContainerFails() public void childrenAreExploredOnDemandEvenIfContainerFails() throws ProcessingException { - final SchemaTree schema = new CanonicalSchemaTree(RAWSCHEMA); + final SchemaTree schema + = new CanonicalSchemaTree(SchemaKey.anonymousKey(), RAWSCHEMA); final JsonTree instance = new SimpleJsonTree(RAWINSTANCE); final FullData data = new FullData(schema, instance, true); final ProcessingReport report = mock(ProcessingReport.class); From 7999395abff0d38a6c957b0ecf823c6152edc8c1 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sun, 25 May 2014 01:35:17 +0200 Subject: [PATCH 37/40] Fuse *.gradle files Signed-off-by: Francis Galiegue --- build.gradle | 43 ++++++++++++++++++++++++++++++++- project.gradle | 64 ------------------------------------------------- settings.gradle | 20 ---------------- 3 files changed, 42 insertions(+), 85 deletions(-) delete mode 100644 project.gradle delete mode 100644 settings.gradle diff --git a/build.gradle b/build.gradle index 5f9fbc12c..4c87e738d 100644 --- a/build.gradle +++ b/build.gradle @@ -65,7 +65,14 @@ apply(plugin: "idea"); apply(plugin: "eclipse"); apply(plugin: "gradle-one-jar"); -apply(from: "project.gradle"); +group = "com.github.fge"; +version = "2.2.5-SNAPSHOT"; +sourceCompatibility = "1.6"; +targetCompatibility = "1.6"; // defaults to sourceCompatibility + +project.ext { + description = "A Java implementation of the JSON Schema specification"; +}; /* * Repositories to use @@ -74,6 +81,40 @@ repositories { mavenCentral(); } +/* + * List of dependencies + */ +dependencies { + compile(group: "com.github.fge", name: "json-schema-core", + version: "1.2.4"); + compile(group: "javax.mail", name: "mailapi", version: "1.4.3"); + compile(group: "joda-time", name: "joda-time", version: "2.3"); + compile(group: "com.googlecode.libphonenumber", name: "libphonenumber", + version: "6.0"); + compile(group: "com.google.code.findbugs", name: "jsr305", + version: "2.0.1"); + compile(group: "net.sf.jopt-simple", name: "jopt-simple", version: "4.6"); + testCompile(group: "org.testng", name: "testng", version: "6.8.7") { + exclude(group: "junit", module: "junit"); + exclude(group: "org.beanshell", module: "bsh"); + exclude(group: "org.yaml", module: "snakeyaml"); + }; + testCompile(group: "org.mockito", name: "mockito-core", version: "1.9.5"); + testCompile(group: "org.easytesting", name: "fest-assert", version: "1.4"); +} + +javadoc.options.links("http://docs.oracle.com/javase/6/docs/api/"); +javadoc.options.links("http://jsr-305.googlecode.com/svn/trunk/javadoc/"); +javadoc.options.links("http://fasterxml.github.com/jackson-databind/javadoc/2.2.0/"); +javadoc.options.links("http://fasterxml.github.com/jackson-core/javadoc/2.2.0/"); +javadoc.options.links("http://docs.guava-libraries.googlecode.com/git-history/v16.0.1/javadoc/"); +javadoc.options.links("http://fge.github.io/btf/"); +javadoc.options.links("http://fge.github.io/msg-simple/"); +javadoc.options.links("http://fge.github.io/jackson-coreutils/"); +javadoc.options.links("http://fge.github.io/uri-template/"); +javadoc.options.links("http://fge.github.io/json-schema-core/1.2.x/"); + + /* * Necessary! Otherwise TestNG will not be used... * diff --git a/project.gradle b/project.gradle deleted file mode 100644 index 345ca5840..000000000 --- a/project.gradle +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2014, Francis Galiegue (fgaliegue@gmail.com) - * - * This software is dual-licensed under: - * - * - the Lesser General Public License (LGPL) version 3.0 or, at your option, any - * later version; - * - the Apache Software License (ASL) version 2.0. - * - * The text of this file and of both licenses is available at the root of this - * project or, if you have the jar distribution, in directory META-INF/, under - * the names LGPL-3.0.txt and ASL-2.0.txt respectively. - * - * Direct link to the sources: - * - * - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt - * - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt - */ - -/* - * Project-specific settings. Unfortunately we cannot put the name in there! - */ -group = "com.github.fge"; -version = "2.2.5-SNAPSHOT"; -sourceCompatibility = "1.6"; -targetCompatibility = "1.6"; // defaults to sourceCompatibility - -project.ext { - description = "A Java implementation of the JSON Schema specification"; -}; - -/* - * List of dependencies - */ -dependencies { - compile(group: "com.github.fge", name: "json-schema-core", - version: "1.2.4"); - compile(group: "javax.mail", name: "mailapi", version: "1.4.3"); - compile(group: "joda-time", name: "joda-time", version: "2.3"); - compile(group: "com.googlecode.libphonenumber", name: "libphonenumber", - version: "6.0"); - compile(group: "com.google.code.findbugs", name: "jsr305", - version: "2.0.1"); - compile(group: "net.sf.jopt-simple", name: "jopt-simple", version: "4.6"); - testCompile(group: "org.testng", name: "testng", version: "6.8.7") { - exclude(group: "junit", module: "junit"); - exclude(group: "org.beanshell", module: "bsh"); - exclude(group: "org.yaml", module: "snakeyaml"); - }; - testCompile(group: "org.mockito", name: "mockito-core", version: "1.9.5"); - testCompile(group: "org.easytesting", name: "fest-assert", version: "1.4"); -} - -javadoc.options.links("http://docs.oracle.com/javase/6/docs/api/"); -javadoc.options.links("http://jsr-305.googlecode.com/svn/trunk/javadoc/"); -javadoc.options.links("http://fasterxml.github.com/jackson-databind/javadoc/2.2.0/"); -javadoc.options.links("http://fasterxml.github.com/jackson-core/javadoc/2.2.0/"); -javadoc.options.links("http://docs.guava-libraries.googlecode.com/git-history/v16.0.1/javadoc/"); -javadoc.options.links("http://fge.github.io/btf/"); -javadoc.options.links("http://fge.github.io/msg-simple/"); -javadoc.options.links("http://fge.github.io/jackson-coreutils/"); -javadoc.options.links("http://fge.github.io/uri-template/"); -javadoc.options.links("http://fge.github.io/json-schema-core/1.2.x/"); - diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 4c0aacfa0..000000000 --- a/settings.gradle +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2014, Francis Galiegue (fgaliegue@gmail.com) - * - * This software is dual-licensed under: - * - * - the Lesser General Public License (LGPL) version 3.0 or, at your option, any - * later version; - * - the Apache Software License (ASL) version 2.0. - * - * The text of this file and of both licenses is available at the root of this - * project or, if you have the jar distribution, in directory META-INF/, under - * the names LGPL-3.0.txt and ASL-2.0.txt respectively. - * - * Direct link to the sources: - * - * - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt - * - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt - */ - -rootProject.name = "json-schema-validator"; From 0945890653b9f3483220119eb2f2fbfa571ce5ff Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sun, 25 May 2014 01:57:11 +0200 Subject: [PATCH 38/40] build.gradle: include Main-Class in jar Although it only makes sense for the -lib jar; however this avoids to use both one-jar and the libJar task; the former is now dropped entirely. Signed-off-by: Francis Galiegue --- build.gradle | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/build.gradle b/build.gradle index 4c87e738d..13c979ab2 100644 --- a/build.gradle +++ b/build.gradle @@ -35,27 +35,6 @@ * * https://github.com/spring-projects/gradle-plugins/tree/master/propdeps-plugin */ -buildscript { - repositories { - mavenCentral(); -// maven { -// url "http://repo.springsource.org/plugins-release"; -// } - } - dependencies { - classpath(group: "com.github.rholder", name: "gradle-one-jar", - version: "1.0.4"); -// classpath(group: "org.springframework.build.gradle", -// name: "propdeps-plugin", version: "0.0.5"); - } -}; - -//configure(allprojects) { -// apply(plugin: "propdeps"); -// apply(plugin: "propdeps-maven"); -// apply(plugin: "propdeps-idea"); -// apply(plugin: "propdeps-eclipse"); -//} apply(plugin: "java"); apply(plugin: "maven"); @@ -63,7 +42,6 @@ apply(plugin: "signing"); apply(plugin: "osgi"); apply(plugin: "idea"); apply(plugin: "eclipse"); -apply(plugin: "gradle-one-jar"); group = "com.github.fge"; version = "2.2.5-SNAPSHOT"; @@ -164,11 +142,6 @@ task javadocJar(type: Jar, dependsOn: copyDocFiles) { from javadoc.destinationDir; } -task fullJar(type: OneJar) { - mainClass = "com.github.fge.jsonschema.main.cli.Main"; - archiveName = "jsonschema.jar"; -} - /* * Creates a jar that can be used as a library on java projects. * This jar already includes all the dependencies. @@ -181,11 +154,15 @@ task libJar(type: Jar, dependsOn: jar) { with jar; } +jar { + manifest { + attributes("Main-Class": "com.github.fge.jsonschema.main.cli.Main"); + } +} artifacts { archives jar; archives sourcesJar; archives javadocJar; - archives fullJar; archives libJar; } From e49cf7f820696d632329b49a0df1c9b84a451945 Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sun, 25 May 2014 01:59:05 +0200 Subject: [PATCH 39/40] Fill release notes Signed-off-by: Francis Galiegue --- RELEASE-NOTES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index a59a0c25d..857d9a671 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,3 +1,9 @@ +### 2.2.5 + +* Fix issue #102: detect, and fail on, circular validation. +* Simplify ValidationProcessor. +* Remove one-jar generation; the -lib jar now includes Main-Class. + ### 2.2.4 * Add a "lib" target to the build. From 53316ef7b1fbb0576985f6454054f396d64a915b Mon Sep 17 00:00:00 2001 From: Francis Galiegue Date: Sun, 25 May 2014 01:59:42 +0200 Subject: [PATCH 40/40] 2.2.5 Francis Galiegue (22): Bump version number Depend on local -SNAPSHOT Make test to reproduce issue #102 SchemaContextEquivalence: remove reference to SchemaTreeEquivalence ValidationChain: drop use of SchemaTreeEquivalence Modify tests; add a message for validation loops ValidationProcessor: don't cache {Array,Object}SchemaSelector results anymore Deprecate a constructor of FullData New class InstanceValidator ValidationProcessor: receive the cached processor directly from the factory Fix issue #102 Improve error message InstanceValidator: improve test, documentation, error message More documentation One more message change Depend on -core 1.2.4 Remove obsolete API usages Remove deprecated API usage Fuse *.gradle files build.gradle: include Main-Class in jar Fill release notes 2.2.5 Signed-off-by: Francis Galiegue --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 13c979ab2..d8c7b3b89 100644 --- a/build.gradle +++ b/build.gradle @@ -44,7 +44,7 @@ apply(plugin: "idea"); apply(plugin: "eclipse"); group = "com.github.fge"; -version = "2.2.5-SNAPSHOT"; +version = "2.2.5"; sourceCompatibility = "1.6"; targetCompatibility = "1.6"; // defaults to sourceCompatibility