From 5f6c1a59e5f11c2020dafc2f4d51293287dc5aaa Mon Sep 17 00:00:00 2001 From: capstan Date: Wed, 27 May 2020 12:12:56 +0200 Subject: [PATCH] Add Equivalence implementation back in a separate target. Continuing on from commit 97eeb08, add a new JsonNumEquivalance class to provide Guava Equivalence, but in a separate jar with separate Guava dependencies. This will allow json-schema-core to continue to use a standard Equivalence provider. --- build.gradle | 337 +++++++++--------- equivalence/project.gradle | 54 +++ .../fge/jackson/JsonNumEquivalence.java | 46 +++ .../fge/jackson/JsonNumEquivalenceTest.java | 95 +++++ equivalence/src/test/resources/testfile.json | 31 ++ settings.gradle | 3 + 6 files changed, 398 insertions(+), 168 deletions(-) create mode 100644 equivalence/project.gradle create mode 100644 equivalence/src/main/java/com/github/fge/jackson/JsonNumEquivalence.java create mode 100644 equivalence/src/test/java/com/github/fge/jackson/JsonNumEquivalenceTest.java create mode 100644 equivalence/src/test/resources/testfile.json diff --git a/build.gradle b/build.gradle index 2d7f739..a679f48 100644 --- a/build.gradle +++ b/build.gradle @@ -18,86 +18,86 @@ */ buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'biz.aQute.bnd:biz.aQute.bnd.gradle:4.2.0' - } + repositories { + mavenCentral() + } + dependencies { + classpath 'biz.aQute.bnd:biz.aQute.bnd.gradle:4.2.0' + } } plugins { id("net.ltgt.errorprone") version "0.8.1" apply false } -apply(plugin: "java"); -apply(plugin: "maven"); -apply(plugin: "signing"); -apply(plugin: "biz.aQute.bnd.builder"); -apply(plugin: "idea"); -apply(plugin: "eclipse"); -apply(plugin: "net.ltgt.errorprone"); +allprojects { + apply(plugin: "java"); + apply(plugin: "maven"); + apply(plugin: "signing"); + apply(plugin: "biz.aQute.bnd.builder"); + apply(plugin: "idea"); + apply(plugin: "eclipse"); + apply(plugin: "net.ltgt.errorprone"); -apply(from: "project.gradle"); + apply(from: "project.gradle"); -group = "com.github.java-json-tools"; + group = "com.github.java-json-tools"; -ext.forRelease = !version.endsWith("-SNAPSHOT"); + ext.forRelease = !version.endsWith("-SNAPSHOT"); -/* - * Repositories to use - */ -repositories { - mavenCentral(); - if (!forRelease) { - maven { - url "https://oss.sonatype.org/content/repositories/snapshots" + /* + * Repositories to use + */ + repositories { + mavenCentral(); + if (!forRelease) { + maven { + url "https://oss.sonatype.org/content/repositories/snapshots" + } } - } - /* Allow staging references for last pre-release testing. */ - if (project.properties.containsKey("sonatypeUsername")) { - maven { - url "https://oss.sonatype.org/service/local/staging/deploy/maven2" - credentials { - username = project.properties["sonatypeUsername"] - password = project.properties["sonatypePassword"] + /* Allow staging references for last pre-release testing. */ + if (project.properties.containsKey("sonatypeUsername")) { + maven { + url "https://oss.sonatype.org/service/local/staging/deploy/maven2" + credentials { + username = project.properties["sonatypeUsername"] + password = project.properties["sonatypePassword"] + } } } } -} -/* - * Add errorprone checking. - */ -dependencies { - errorprone("com.google.errorprone:error_prone_core:2.3.3") - errorproneJavac("com.google.errorprone:javac:9+181-r4173-1") -} + /* + * Add errorprone checking. + */ + dependencies { + errorprone("com.google.errorprone:error_prone_core:2.3.3") + errorproneJavac("com.google.errorprone:javac:9+181-r4173-1") + } -/* - * Necessary! Otherwise TestNG will not be used... - * - * Also, we don't want gradle's default HTML report: it does not support - * parameterized tests which I use a _lot_. - */ -test { - useTestNG() { - useDefaultListeners = true; - }; -} + /* + * Necessary! Otherwise TestNG will not be used... + * + * Also, we don't want gradle's default HTML report: it does not support + * parameterized tests which I use a _lot_. + */ + test { + useTestNG() { + useDefaultListeners = true; + }; + } -/* - * Necessary to generate the source and javadoc jars - */ -task sourcesJar(type: Jar, dependsOn: classes) { - classifier = "sources"; - from sourceSets.main.allSource; -} + /* + * Necessary to generate the source and javadoc jars + */ + task sourcesJar(type: Jar, dependsOn: classes) { + classifier = "sources"; + from sourceSets.main.allSource; + } -/* - * Lint all the things! - */ -allprojects { + /* + * Lint all the things! + */ gradle.projectsEvaluated { tasks.withType(JavaCompile) { options.compilerArgs << "-Xlint:all" << "-Werror" @@ -106,136 +106,137 @@ allprojects { options.addStringOption('Xwerror', '-quiet') } } -} -/* - * Javadoc: we need to tell where the overview.html is, it will not pick it up - * automatically... - */ + task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = "javadoc"; + from javadoc.destinationDir; + } -javadoc { - options.overview = "src/main/java/overview.html"; -} + artifacts { + archives jar; + archives sourcesJar; + archives javadocJar; + } -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = "javadoc"; - from javadoc.destinationDir; -} + wrapper { + gradleVersion = "5.6.3"; + distributionUrl = "https://services.gradle.org/distributions/gradle-${gradleVersion}-all.zip"; + } -artifacts { - archives jar; - archives sourcesJar; - archives javadocJar; -} + task pom { + doLast { + pom {}.writeTo("${projectDir}/pom.xml"); + } + } -wrapper { - gradleVersion = "5.6.3"; - distributionUrl = "https://services.gradle.org/distributions/gradle-${gradleVersion}-all.zip"; -} + /* + * SIGNING + */ -task pom { - doLast { - pom {}.writeTo("${projectDir}/pom.xml"); - } -} + project.ext { + description = "JSON Pointer (RFC 6901) and numeric equality for Jackson (2.2.x)"; + scmUrl = sprintf("git@github.com:java-json-tools/%s.git", rootProject.name) + projectURL = sprintf("https://github.com/java-json-tools/%s", rootProject.name); + sonatypeStaging = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"; + sonatypeSnapshots = "https://oss.sonatype.org/content/repositories/snapshots/"; + }; -/* - * SIGNING - */ + task checkSigningRequirements { + doLast { + def requiredProperties = [ "sonatypeUsername", "sonatypePassword" ]; + def noDice = false; + requiredProperties.each { + if (project.properties[it] == null) { + noDice = true; + System.err.printf("property \"%s\" is not defined!") + } + } + if (noDice) + throw new IllegalStateException("missing required properties for " + + "upload"); + } + } -project.ext { - description = "JSON Pointer (RFC 6901) and numeric equality for Jackson (2.2.x)"; - scmUrl = sprintf("git@github.com:java-json-tools/%s.git", name) - projectURL = sprintf("https://github.com/java-json-tools/%s", name); - sonatypeStaging = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"; - sonatypeSnapshots = "https://oss.sonatype.org/content/repositories/snapshots/"; -}; - -task checkSigningRequirements { - doLast { - def requiredProperties = [ "sonatypeUsername", "sonatypePassword" ]; - def noDice = false; - requiredProperties.each { - if (project.properties[it] == null) { - noDice = true; - System.err.printf("property \"%s\" is not defined!") + uploadArchives { + dependsOn(checkSigningRequirements); + repositories { + mavenDeployer { + beforeDeployment { + MavenDeployment deployment -> signing.signPom(deployment); + } + + repository(url: "${sonatypeStaging}") { + authentication( + userName: project.properties["sonatypeUsername"], + password: project.properties["sonatypePassword"] + ); + } + + snapshotRepository(url: "${sonatypeSnapshots}") { + authentication( + userName: project.properties["sonatypeUsername"], + password: project.properties["sonatypePassword"] + ); + } } } - if (noDice) - throw new IllegalStateException("missing required properties for " + - "upload"); } -} -uploadArchives { - dependsOn(checkSigningRequirements); - repositories { - mavenDeployer { - beforeDeployment { - MavenDeployment deployment -> signing.signPom(deployment); + /* + * Configure pom.xml on install, uploadArchives + */ + [ + install.repositories.mavenInstaller, + uploadArchives.repositories.mavenDeployer + ]*.pom*.whenConfigured { pom -> + pom.project { + name "${project.name}"; + packaging "jar"; + description "${project.ext.description}"; + url "${projectURL}"; + + scm { + url "${scmUrl}"; + connection "${scmUrl}"; + developerConnection "scm:git:${scmUrl}"; } - repository(url: "${sonatypeStaging}") { - authentication( - userName: project.properties["sonatypeUsername"], - password: project.properties["sonatypePassword"] - ); + licenses { + license { + name "Lesser General Public License, version 3 or greater"; + url "http://www.gnu.org/licenses/lgpl.html"; + distribution "repo"; + }; + license { + name "Apache Software License, version 2.0"; + url "http://www.apache.org/licenses/LICENSE-2.0"; + distribution "repo"; + } } - snapshotRepository(url: "${sonatypeSnapshots}") { - authentication( - userName: project.properties["sonatypeUsername"], - password: project.properties["sonatypePassword"] - ); + developers { + developer { + id "huggsboson"; + name "John Huffaker"; + email "jhuffaker+java-json-tools@gmail.com"; + } } } } + + signing { + required { forRelease && gradle.taskGraph.hasTask("uploadArchives") }; + sign configurations.archives; + } } /* - * Configure pom.xml on install, uploadArchives + * Javadoc: we need to tell where the overview.html is, it will not pick it up + * automatically... */ -[ - install.repositories.mavenInstaller, - uploadArchives.repositories.mavenDeployer -]*.pom*.whenConfigured { pom -> - pom.project { - name "${project.name}"; - packaging "jar"; - description "${project.ext.description}"; - url "${projectURL}"; - - scm { - url "${scmUrl}"; - connection "${scmUrl}"; - developerConnection "scm:git:${scmUrl}"; - } - - licenses { - license { - name "Lesser General Public License, version 3 or greater"; - url "http://www.gnu.org/licenses/lgpl.html"; - distribution "repo"; - }; - license { - name "Apache Software License, version 2.0"; - url "http://www.apache.org/licenses/LICENSE-2.0"; - distribution "repo"; - } - } - developers { - developer { - id "huggsboson"; - name "John Huffaker"; - email "jhuffaker+java-json-tools@gmail.com"; - } - } - } +javadoc { + options.overview = "src/main/java/overview.html"; } -signing { - required { forRelease && gradle.taskGraph.hasTask("uploadArchives") }; - sign configurations.archives; -} diff --git a/equivalence/project.gradle b/equivalence/project.gradle new file mode 100644 index 0000000..6e5ce37 --- /dev/null +++ b/equivalence/project.gradle @@ -0,0 +1,54 @@ +/* + * 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.java-json-tools"; +version = "1.0-SNAPSHOT"; +sourceCompatibility = JavaVersion.VERSION_1_7; +targetCompatibility = JavaVersion.VERSION_1_7; // defaults to sourceCompatibility + +/* + * List of dependencies + */ +dependencies { + implementation(group: "com.fasterxml.jackson.core", name: "jackson-databind", version: "2.11.0"); + implementation(group: "com.google.guava", name: "guava", version: "28.2-android"); + implementation(group: "com.github.java-json-tools", name: "jackson-coreutils", version: "2.0-SNAPSHOT"); + testImplementation(group: "org.testng", name: "testng", version: "7.1.0") { + exclude(group: "junit", module: "junit"); + exclude(group: "org.beanshell", module: "bsh"); + exclude(group: "org.yaml", module: "snakeyaml"); + }; +} + +javadoc { + options { + def currentJavaVersion = org.gradle.api.JavaVersion.current() + // FIXME: https://github.com/gradle/gradle/issues/11182 + if (currentJavaVersion.compareTo(org.gradle.api.JavaVersion.VERSION_1_9) >= 0) { + addStringOption("-release", "7"); + } + links("https://docs.oracle.com/javase/7/docs/api/"); + links("https://fasterxml.github.io/jackson-databind/javadoc/2.11/"); + links("https://www.javadoc.io/doc/com.google.guava/guava/28.2-android/"); + links("https://java-json-tools.github.io/jackson-coreutils/"); + } +} diff --git a/equivalence/src/main/java/com/github/fge/jackson/JsonNumEquivalence.java b/equivalence/src/main/java/com/github/fge/jackson/JsonNumEquivalence.java new file mode 100644 index 0000000..29c2618 --- /dev/null +++ b/equivalence/src/main/java/com/github/fge/jackson/JsonNumEquivalence.java @@ -0,0 +1,46 @@ +/* + * 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.jackson; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.base.Equivalence; + +/** + * An {@link Equivalence} strategy for JSON Schema equality + */ +public final class JsonNumEquivalence + extends Equivalence +{ + private static final Equivalence INSTANCE + = new JsonNumEquivalence(); + + public static Equivalence getInstance() { + return INSTANCE; + } + + @Override + protected boolean doEquivalent(final JsonNode a, final JsonNode b) { + return JsonNumEquals.getInstance().equivalent(a, b); + } + + @Override + protected int doHash(final JsonNode t) { + return JsonNumEquals.getInstance().hash(t); + } +} diff --git a/equivalence/src/test/java/com/github/fge/jackson/JsonNumEquivalenceTest.java b/equivalence/src/test/java/com/github/fge/jackson/JsonNumEquivalenceTest.java new file mode 100644 index 0000000..939778b --- /dev/null +++ b/equivalence/src/test/java/com/github/fge/jackson/JsonNumEquivalenceTest.java @@ -0,0 +1,95 @@ +/* + * 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.jackson; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import static org.testng.Assert.*; + +public final class JsonNumEquivalenceTest +{ + private static final JsonNodeFactory FACTORY = JsonNodeFactory.instance; + private JsonNode testData; + + @BeforeClass + public void initData() + throws IOException + { + testData = JsonLoader.fromResource("/testfile.json"); + } + + @DataProvider + public Iterator getInputs() + { + final List list = new ArrayList<>(); + + JsonNode reference; + + for (final JsonNode element: testData) { + reference = element.get("reference"); + for (final JsonNode node: element.get("equivalences")) + list.add(new Object[]{reference, node}); + } + + return list.iterator(); + } + + @Test(dataProvider = "getInputs") + public void numericEqualityIsAcknowledged(final JsonNode reference, + final JsonNode node) + { + assertTrue(JsonNumEquals.getInstance().equivalent(reference, node)); + } + + @Test(dataProvider = "getInputs") + public void numericEqualityWorksWithinArrays(final JsonNode reference, + final JsonNode node) + { + final ArrayNode node1 = FACTORY.arrayNode(); + node1.add(reference); + final ArrayNode node2 = FACTORY.arrayNode(); + node2.add(node); + + assertTrue(JsonNumEquals.getInstance().equivalent(node1, node2)); + } + + @Test(dataProvider = "getInputs") + public void numericEqualityWorksWithinObjects(final JsonNode reference, + final JsonNode node) + { + final ObjectNode node1 = FACTORY.objectNode(); + node1.set("foo", reference); + final ObjectNode node2 = FACTORY.objectNode(); + node2.set("foo", node); + + assertTrue(JsonNumEquals.getInstance().equivalent(node1, node2)); + } +} diff --git a/equivalence/src/test/resources/testfile.json b/equivalence/src/test/resources/testfile.json new file mode 100644 index 0000000..6dca46d --- /dev/null +++ b/equivalence/src/test/resources/testfile.json @@ -0,0 +1,31 @@ +[ + { + "reference": 0, + "equivalences": [ + 0.0, + 0.000000, + 0e2, + -0 + ] + }, + { + "reference": 0.1, + "equivalences": [ + 0.10, + 1e-1, + 10.000e-2, + 0.00100e2 + ] + }, + { + "reference": 1, + "equivalences": [ + 10e-1, + 1.0, + 1.0000000, + 100E-2, + 0.1E1, + 0.000000000000001e15 + ] + } +] \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 6be666e..77f1677 100644 --- a/settings.gradle +++ b/settings.gradle @@ -18,3 +18,6 @@ */ rootProject.name = "jackson-coreutils"; +include "equivalence"; +project(":equivalence").name = "jackson-coreutils-equivalence"; +