From cf449b88ab0277ba69e5700faac0e28703d8b0bd Mon Sep 17 00:00:00 2001 From: Abel Keszei Date: Sun, 30 Jan 2022 22:06:11 +0100 Subject: [PATCH 01/12] add gson support to json extension --- .../diffplug/spotless/json/gson/GsonStep.java | 74 +++++++++++++++++++ .../spotless/json/gson/GsonWrapper.java | 33 +++++++++ .../spotless/json/gson/GsonWrapperBase.java | 56 ++++++++++++++ .../json/gson/JsonElementWrapper.java | 27 +++++++ .../spotless/json/gson/JsonObjectWrapper.java | 46 ++++++++++++ .../spotless/json/gson/JsonWriterWrapper.java | 33 +++++++++ .../gradle/spotless/JsonExtension.java | 41 ++++++++++ 7 files changed, 310 insertions(+) create mode 100644 lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java create mode 100644 lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapper.java create mode 100644 lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapperBase.java create mode 100644 lib/src/main/java/com/diffplug/spotless/json/gson/JsonElementWrapper.java create mode 100644 lib/src/main/java/com/diffplug/spotless/json/gson/JsonObjectWrapper.java create mode 100644 lib/src/main/java/com/diffplug/spotless/json/gson/JsonWriterWrapper.java diff --git a/lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java new file mode 100644 index 000000000..503595328 --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java @@ -0,0 +1,74 @@ +package com.diffplug.spotless.json.gson; + +import com.diffplug.spotless.FormatterFunc; +import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.JarState; +import com.diffplug.spotless.Provisioner; + +import java.io.IOException; +import java.io.Serializable; +import java.io.StringWriter; +import java.util.Collections; +import java.util.Objects; + +public class GsonStep { + private static final String MAVEN_COORDINATES = "com.google.code.gson:gson"; + + public static FormatterStep create(int indentSpaces, boolean sortByKeys, String version, Provisioner provisioner) { + Objects.requireNonNull(provisioner, "provisioner cannot be null"); + return FormatterStep.createLazy("gson", () -> new State(indentSpaces, sortByKeys, version, provisioner), State::toFormatter); + } + + private static final class State implements Serializable { + private static final long serialVersionUID = 6540876150977107116L; + + private final int indentSpaces; + private final boolean sortByKeys; + private final JarState jarState; + + private State(int indentSpaces, boolean sortByKeys, String version, Provisioner provisioner) throws IOException { + this.indentSpaces = indentSpaces; + this.sortByKeys = sortByKeys; + this.jarState = JarState.from(MAVEN_COORDINATES + ":" + version, provisioner); + } + + FormatterFunc toFormatter() { + JsonWriterWrapper jsonWriterWrapper = new JsonWriterWrapper(jarState); + JsonElementWrapper jsonElementWrapper = new JsonElementWrapper(jarState); + JsonObjectWrapper jsonObjectWrapper = new JsonObjectWrapper(jarState, jsonElementWrapper); + GsonWrapper gsonWrapper = new GsonWrapper(jarState, jsonElementWrapper, jsonWriterWrapper); + Object gson = gsonWrapper.createGson(); + + return inputString -> { + Object jsonElement = gsonWrapper.fromJson(gson, inputString, jsonElementWrapper.getWrappedClass()); + if (sortByKeys && jsonElementWrapper.isJsonObject(jsonElement)) { + jsonElement = sortByKeys(jsonObjectWrapper, jsonElementWrapper, jsonElement); + } + try (StringWriter stringWriter = new StringWriter()) { + Object jsonWriter = jsonWriterWrapper.createJsonWriter(stringWriter); + jsonWriterWrapper.setIndent(jsonWriter, generateIndent(indentSpaces)); + gsonWrapper.toJson(gson, jsonElement, jsonWriter); + return stringWriter.toString(); + } + }; + } + + private Object sortByKeys(JsonObjectWrapper jsonObjectWrapper, JsonElementWrapper jsonElementWrapper, Object jsonObject) { + Object result = jsonObjectWrapper.createJsonObject(); + jsonObjectWrapper.keySet(jsonObject).stream().sorted() + .forEach(key -> { + Object element = jsonObjectWrapper.get(jsonObject, key); + if (jsonElementWrapper.isJsonObject(element)) { + element = sortByKeys(jsonObjectWrapper, jsonElementWrapper, element); + } + jsonObjectWrapper.add(result, key, element); + }); + return result; + } + + private String generateIndent(int indentSpaces) { + return String.join("", Collections.nCopies(indentSpaces, " ")); + } + } + +} diff --git a/lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapper.java b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapper.java new file mode 100644 index 000000000..53e765fa0 --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapper.java @@ -0,0 +1,33 @@ +package com.diffplug.spotless.json.gson; + +import com.diffplug.spotless.JarState; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +public class GsonWrapper extends GsonWrapperBase { + + private final Constructor constructor; + private final Method fromJsonMethod; + private final Method toJsonMethod; + + public GsonWrapper(JarState jarState, JsonElementWrapper jsonElementWrapper, JsonWriterWrapper jsonWriterWrapper) { + Class clazz = loadClass(jarState.getClassLoader(), "com.google.gson.Gson"); + this.constructor = getConstructor(clazz); + this.fromJsonMethod = getMethod(clazz, "fromJson", String.class, Class.class); + this.toJsonMethod = getMethod(clazz, "toJson", jsonElementWrapper.getWrappedClass(), jsonWriterWrapper.getWrappedClass()); + } + + public Object createGson() { + return newInstance(constructor); + } + + public Object fromJson(Object gson, String json, Class type) { + return invoke(fromJsonMethod, gson, json, type); + } + + public void toJson(Object gson, Object jsonElement, Object jsonWriter) { + invoke(toJsonMethod, gson, jsonElement, jsonWriter); + } + +} diff --git a/lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapperBase.java b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapperBase.java new file mode 100644 index 000000000..182011c96 --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapperBase.java @@ -0,0 +1,56 @@ +package com.diffplug.spotless.json.gson; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public abstract class GsonWrapperBase { + + public static final String INCOMPATIBLE_ERROR_MESSAGE = "There was a problem interacting with Gson; maybe you set an incompatible version?"; + public static final String FAILED_TO_PARSE_ERROR_MESSAGE = "Failed to parse JSON"; + + protected final Class loadClass(ClassLoader classLoader, String className) { + try { + return classLoader.loadClass(className); + } catch (ClassNotFoundException cause) { + throw new IllegalStateException(INCOMPATIBLE_ERROR_MESSAGE, cause); + } + } + + protected final Constructor getConstructor(Class clazz, Class... argumentTypes) { + try { + return clazz.getConstructor(argumentTypes); + } catch (NoSuchMethodException cause) { + throw new IllegalStateException(INCOMPATIBLE_ERROR_MESSAGE, cause); + } + } + + protected final Method getMethod(Class clazz, String name, Class... argumentTypes) { + try { + return clazz.getMethod(name, argumentTypes); + } catch (NoSuchMethodException cause) { + throw new IllegalStateException(INCOMPATIBLE_ERROR_MESSAGE, cause); + } + } + + protected final T newInstance(Constructor constructor, Object... args) { + try { + return constructor.newInstance(args); + } catch (InstantiationException | IllegalAccessException cause) { + throw new IllegalStateException(INCOMPATIBLE_ERROR_MESSAGE, cause); + } catch (InvocationTargetException cause) { + throw new IllegalStateException(FAILED_TO_PARSE_ERROR_MESSAGE, cause.getCause()); + } + } + + protected Object invoke(Method method, Object targetObject, Object... args) { + try { + return method.invoke(targetObject, args); + } catch (IllegalAccessException cause) { + throw new IllegalStateException(INCOMPATIBLE_ERROR_MESSAGE, cause); + } catch (InvocationTargetException cause) { + throw new IllegalStateException(FAILED_TO_PARSE_ERROR_MESSAGE, cause.getCause()); + } + } + +} diff --git a/lib/src/main/java/com/diffplug/spotless/json/gson/JsonElementWrapper.java b/lib/src/main/java/com/diffplug/spotless/json/gson/JsonElementWrapper.java new file mode 100644 index 000000000..a45225b41 --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/json/gson/JsonElementWrapper.java @@ -0,0 +1,27 @@ +package com.diffplug.spotless.json.gson; + +import com.diffplug.spotless.JarState; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Set; + +public class JsonElementWrapper extends GsonWrapperBase { + + private final Class clazz; + private final Method isJsonObjectMethod; + + public JsonElementWrapper(JarState jarState) { + this.clazz = loadClass(jarState.getClassLoader(), "com.google.gson.JsonElement"); + this.isJsonObjectMethod = getMethod(clazz, "isJsonObject"); + } + + public boolean isJsonObject(Object jsonElement) { + return (boolean) invoke(isJsonObjectMethod, jsonElement); + } + + public Class getWrappedClass() { + return clazz; + } + +} diff --git a/lib/src/main/java/com/diffplug/spotless/json/gson/JsonObjectWrapper.java b/lib/src/main/java/com/diffplug/spotless/json/gson/JsonObjectWrapper.java new file mode 100644 index 000000000..7d3151b48 --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/json/gson/JsonObjectWrapper.java @@ -0,0 +1,46 @@ +package com.diffplug.spotless.json.gson; + +import com.diffplug.spotless.JarState; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Set; + +public class JsonObjectWrapper extends GsonWrapperBase { + + private final Class clazz; + private final Constructor constructor; + private final Method keySetMethod; + private final Method getMethod; + private final Method addMethod; + + public JsonObjectWrapper(JarState jarState, JsonElementWrapper jsonElementWrapper) { + this.clazz = loadClass(jarState.getClassLoader(), "com.google.gson.JsonObject"); + this.constructor = getConstructor(clazz); + this.keySetMethod = getMethod(clazz, "keySet"); + this.getMethod = getMethod(clazz, "get", String.class); + this.addMethod = getMethod(clazz, "add", String.class, jsonElementWrapper.getWrappedClass()); + } + + public Object createJsonObject() { + return newInstance(constructor); + } + + @SuppressWarnings("unchecked") + public Set keySet(Object jsonObject) { + return (Set) invoke(keySetMethod, jsonObject); + } + + public Object get(Object jsonObject, String key) { + return invoke(getMethod, jsonObject, key); + } + + public void add(Object jsonObject, String key, Object element) { + invoke(addMethod, jsonObject, key, element); + } + + public Class getWrappedClass() { + return clazz; + } + +} diff --git a/lib/src/main/java/com/diffplug/spotless/json/gson/JsonWriterWrapper.java b/lib/src/main/java/com/diffplug/spotless/json/gson/JsonWriterWrapper.java new file mode 100644 index 000000000..580e075e7 --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/json/gson/JsonWriterWrapper.java @@ -0,0 +1,33 @@ +package com.diffplug.spotless.json.gson; + +import com.diffplug.spotless.JarState; + +import java.io.Writer; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +public class JsonWriterWrapper extends GsonWrapperBase { + + private final Class clazz; + private final Constructor constructor; + private final Method setIndentMethod; + + public JsonWriterWrapper(JarState jarState) { + this.clazz = loadClass(jarState.getClassLoader(), "com.google.gson.stream.JsonWriter"); + this.constructor = getConstructor(clazz, Writer.class); + this.setIndentMethod = getMethod(clazz, "setIndent", String.class); + } + + public Object createJsonWriter(Writer writer) { + return newInstance(constructor, writer); + } + + public void setIndent(Object jsonWriter, String indent) { + invoke(setIndentMethod, jsonWriter, indent); + } + + public Class getWrappedClass() { + return clazz; + } + +} diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JsonExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JsonExtension.java index e15d395f3..e3aeaca9d 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JsonExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JsonExtension.java @@ -19,9 +19,11 @@ import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.json.JsonSimpleStep; +import com.diffplug.spotless.json.gson.GsonStep; public class JsonExtension extends FormatExtension { private static final int DEFAULT_INDENTATION = 4; + private static final String DEFAULT_GSON_VERSION = "2.8.9"; static final String NAME = "json"; @Inject @@ -41,6 +43,10 @@ public SimpleConfig simple() { return new SimpleConfig(DEFAULT_INDENTATION); } + public GsonConfig gson() { + return new GsonConfig(); + } + public class SimpleConfig { private int indent; @@ -59,4 +65,39 @@ private FormatterStep createStep() { } } + public class GsonConfig { + private int indentSpaces; + private boolean sortByKeys; + private String version; + + public GsonConfig() { + this.indentSpaces = DEFAULT_INDENTATION; + this.sortByKeys = false; + this.version = DEFAULT_GSON_VERSION; + addStep(createStep()); + } + + public GsonConfig indentWithSpaces(int indentSpaces) { + this.indentSpaces = indentSpaces; + replaceStep(createStep()); + return this; + } + + public GsonConfig sortByKeys() { + this.sortByKeys = true; + replaceStep(createStep()); + return this; + } + + public GsonConfig version(String version) { + this.version = version; + replaceStep(createStep()); + return this; + } + + private FormatterStep createStep() { + return GsonStep.create(indentSpaces, sortByKeys, version, provisioner()); + } + } + } From ecbbda3113a7955874d206a39d227a1e927f2a20 Mon Sep 17 00:00:00 2001 From: Abel Keszei Date: Sun, 30 Jan 2022 22:21:27 +0100 Subject: [PATCH 02/12] add gson support to README --- plugin-gradle/README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index ffc52bd92..b3821d527 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -595,6 +595,7 @@ spotless { simple() // has its own section below prettier().config(['parser': 'json']) // see Prettier section below eclipseWtp('json') // see Eclipse web tools platform section + gson() // has its own section below } } ``` @@ -614,6 +615,22 @@ spotless { } ``` +### Gson + +Uses Google Gson to also allow sorting by keys besides custom indentation - useful for i18n files. + +```gradle +spotless { + json { + target 'src/**/*.json' + gson() + .indentWithSpaces(6) // optional: specify the number of spaces to use + .sortByKeys() // optional: sort JSON by its keys + .version('2.8.1') // optional: specify version + } +} +``` + ## Prettier From a62ff120995b09ba9f7a018ac01b61ea7466da7c Mon Sep 17 00:00:00 2001 From: Abel Keszei Date: Sat, 12 Feb 2022 18:03:04 +0100 Subject: [PATCH 03/12] update unit tests for gson support --- .../json/gson/GsonBuilderWrapper.java | 39 ++ .../diffplug/spotless/json/gson/GsonStep.java | 45 +- .../spotless/json/gson/GsonWrapperBase.java | 6 +- .../spotless/json/gson/JsonObjectWrapper.java | 7 +- .../gradle/spotless/JsonExtension.java | 10 +- .../json/cucumberJsonSampleGsonAfter.json | 660 ++++++++++++++++++ .../json/cucumberJsonSampleGsonBefore.json | 660 ++++++++++++++++++ .../resources/json/escapeHtmlGsonAfter.json | 4 + .../json/escapeHtmlGsonAfterDisabled.json | 4 + .../resources/json/escapeHtmlGsonBefore.json | 4 + .../json/objectWithNullGsonAfter.json | 4 + .../json/objectWithNullGsonBefore.json | 4 + .../main/resources/json/sortByKeysAfter.json | 19 + .../json/sortByKeysAfterDisabled.json | 19 + .../main/resources/json/sortByKeysBefore.json | 19 + .../diffplug/spotless/json/GsonStepTest.java | 85 +++ .../json/JsonFormatterStepCommonTests.java | 89 +++ .../spotless/json/JsonSimpleStepTest.java | 107 +-- 18 files changed, 1672 insertions(+), 113 deletions(-) create mode 100644 lib/src/main/java/com/diffplug/spotless/json/gson/GsonBuilderWrapper.java create mode 100644 testlib/src/main/resources/json/cucumberJsonSampleGsonAfter.json create mode 100644 testlib/src/main/resources/json/cucumberJsonSampleGsonBefore.json create mode 100644 testlib/src/main/resources/json/escapeHtmlGsonAfter.json create mode 100644 testlib/src/main/resources/json/escapeHtmlGsonAfterDisabled.json create mode 100644 testlib/src/main/resources/json/escapeHtmlGsonBefore.json create mode 100644 testlib/src/main/resources/json/objectWithNullGsonAfter.json create mode 100644 testlib/src/main/resources/json/objectWithNullGsonBefore.json create mode 100644 testlib/src/main/resources/json/sortByKeysAfter.json create mode 100644 testlib/src/main/resources/json/sortByKeysAfterDisabled.json create mode 100644 testlib/src/main/resources/json/sortByKeysBefore.json create mode 100644 testlib/src/test/java/com/diffplug/spotless/json/GsonStepTest.java create mode 100644 testlib/src/test/java/com/diffplug/spotless/json/JsonFormatterStepCommonTests.java diff --git a/lib/src/main/java/com/diffplug/spotless/json/gson/GsonBuilderWrapper.java b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonBuilderWrapper.java new file mode 100644 index 000000000..1693a5f9d --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonBuilderWrapper.java @@ -0,0 +1,39 @@ +package com.diffplug.spotless.json.gson; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +import com.diffplug.spotless.JarState; + +public class GsonBuilderWrapper extends GsonWrapperBase { + + private final Constructor constructor; + private final Method serializeNullsMethod; + private final Method disableHtmlEscapingMethod; + private final Method createMethod; + + public GsonBuilderWrapper(JarState jarState) { + Class clazz = loadClass(jarState.getClassLoader(), "com.google.gson.GsonBuilder"); + this.constructor = getConstructor(clazz); + this.serializeNullsMethod = getMethod(clazz, "serializeNulls"); + this.disableHtmlEscapingMethod = getMethod(clazz, "disableHtmlEscaping"); + this.createMethod = getMethod(clazz, "create"); + } + + public Object createGsonBuilder() { + return newInstance(constructor); + } + + public Object serializeNulls(Object gsonBuilder) { + return invoke(serializeNullsMethod, gsonBuilder); + } + + public Object disableHtmlEscaping(Object gsonBuilder) { + return invoke(disableHtmlEscapingMethod, gsonBuilder); + } + + public Object create(Object gsonBuilder) { + return invoke(createMethod, gsonBuilder); + } + +} diff --git a/lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java index 503595328..40ca84eeb 100644 --- a/lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java +++ b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java @@ -14,21 +14,23 @@ public class GsonStep { private static final String MAVEN_COORDINATES = "com.google.code.gson:gson"; - public static FormatterStep create(int indentSpaces, boolean sortByKeys, String version, Provisioner provisioner) { + public static FormatterStep create(int indentSpaces, boolean sortByKeys, boolean escapeHtml, String version, Provisioner provisioner) { Objects.requireNonNull(provisioner, "provisioner cannot be null"); - return FormatterStep.createLazy("gson", () -> new State(indentSpaces, sortByKeys, version, provisioner), State::toFormatter); + return FormatterStep.createLazy("gson", () -> new State(indentSpaces, sortByKeys, escapeHtml, version, provisioner), State::toFormatter); } private static final class State implements Serializable { - private static final long serialVersionUID = 6540876150977107116L; + private static final long serialVersionUID = -1493479043249379485L; private final int indentSpaces; private final boolean sortByKeys; + private final boolean escapeHtml; private final JarState jarState; - private State(int indentSpaces, boolean sortByKeys, String version, Provisioner provisioner) throws IOException { + private State(int indentSpaces, boolean sortByKeys, boolean escapeHtml, String version, Provisioner provisioner) throws IOException { this.indentSpaces = indentSpaces; this.sortByKeys = sortByKeys; + this.escapeHtml = escapeHtml; this.jarState = JarState.from(MAVEN_COORDINATES + ":" + version, provisioner); } @@ -36,20 +38,35 @@ FormatterFunc toFormatter() { JsonWriterWrapper jsonWriterWrapper = new JsonWriterWrapper(jarState); JsonElementWrapper jsonElementWrapper = new JsonElementWrapper(jarState); JsonObjectWrapper jsonObjectWrapper = new JsonObjectWrapper(jarState, jsonElementWrapper); + GsonBuilderWrapper gsonBuilderWrapper = new GsonBuilderWrapper(jarState); GsonWrapper gsonWrapper = new GsonWrapper(jarState, jsonElementWrapper, jsonWriterWrapper); - Object gson = gsonWrapper.createGson(); + + Object gsonBuilder = gsonBuilderWrapper.serializeNulls(gsonBuilderWrapper.createGsonBuilder()); + if (!escapeHtml) { + gsonBuilder = gsonBuilderWrapper.disableHtmlEscaping(gsonBuilder); + } + Object gson = gsonBuilderWrapper.create(gsonBuilder); return inputString -> { - Object jsonElement = gsonWrapper.fromJson(gson, inputString, jsonElementWrapper.getWrappedClass()); - if (sortByKeys && jsonElementWrapper.isJsonObject(jsonElement)) { - jsonElement = sortByKeys(jsonObjectWrapper, jsonElementWrapper, jsonElement); - } - try (StringWriter stringWriter = new StringWriter()) { - Object jsonWriter = jsonWriterWrapper.createJsonWriter(stringWriter); - jsonWriterWrapper.setIndent(jsonWriter, generateIndent(indentSpaces)); - gsonWrapper.toJson(gson, jsonElement, jsonWriter); - return stringWriter.toString(); + String result; + if (inputString.isEmpty()) { + result = ""; + } else { + Object jsonElement = gsonWrapper.fromJson(gson, inputString, jsonElementWrapper.getWrappedClass()); + if (jsonElement == null) { + throw new AssertionError(GsonWrapperBase.FAILED_TO_PARSE_ERROR_MESSAGE); + } + if (sortByKeys && jsonElementWrapper.isJsonObject(jsonElement)) { + jsonElement = sortByKeys(jsonObjectWrapper, jsonElementWrapper, jsonElement); + } + try (StringWriter stringWriter = new StringWriter()) { + Object jsonWriter = jsonWriterWrapper.createJsonWriter(stringWriter); + jsonWriterWrapper.setIndent(jsonWriter, generateIndent(indentSpaces)); + gsonWrapper.toJson(gson, jsonElement, jsonWriter); + result = stringWriter + "\n"; + } } + return result; }; } diff --git a/lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapperBase.java b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapperBase.java index 182011c96..31e22d53d 100644 --- a/lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapperBase.java +++ b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapperBase.java @@ -7,7 +7,7 @@ public abstract class GsonWrapperBase { public static final String INCOMPATIBLE_ERROR_MESSAGE = "There was a problem interacting with Gson; maybe you set an incompatible version?"; - public static final String FAILED_TO_PARSE_ERROR_MESSAGE = "Failed to parse JSON"; + public static final String FAILED_TO_PARSE_ERROR_MESSAGE = "Unable to format JSON"; protected final Class loadClass(ClassLoader classLoader, String className) { try { @@ -39,7 +39,7 @@ protected final T newInstance(Constructor constructor, Object... args) { } catch (InstantiationException | IllegalAccessException cause) { throw new IllegalStateException(INCOMPATIBLE_ERROR_MESSAGE, cause); } catch (InvocationTargetException cause) { - throw new IllegalStateException(FAILED_TO_PARSE_ERROR_MESSAGE, cause.getCause()); + throw new AssertionError(FAILED_TO_PARSE_ERROR_MESSAGE, cause.getCause()); } } @@ -49,7 +49,7 @@ protected Object invoke(Method method, Object targetObject, Object... args) { } catch (IllegalAccessException cause) { throw new IllegalStateException(INCOMPATIBLE_ERROR_MESSAGE, cause); } catch (InvocationTargetException cause) { - throw new IllegalStateException(FAILED_TO_PARSE_ERROR_MESSAGE, cause.getCause()); + throw new AssertionError(FAILED_TO_PARSE_ERROR_MESSAGE, cause.getCause()); } } diff --git a/lib/src/main/java/com/diffplug/spotless/json/gson/JsonObjectWrapper.java b/lib/src/main/java/com/diffplug/spotless/json/gson/JsonObjectWrapper.java index 7d3151b48..5485ce2ff 100644 --- a/lib/src/main/java/com/diffplug/spotless/json/gson/JsonObjectWrapper.java +++ b/lib/src/main/java/com/diffplug/spotless/json/gson/JsonObjectWrapper.java @@ -8,14 +8,13 @@ public class JsonObjectWrapper extends GsonWrapperBase { - private final Class clazz; private final Constructor constructor; private final Method keySetMethod; private final Method getMethod; private final Method addMethod; public JsonObjectWrapper(JarState jarState, JsonElementWrapper jsonElementWrapper) { - this.clazz = loadClass(jarState.getClassLoader(), "com.google.gson.JsonObject"); + Class clazz = loadClass(jarState.getClassLoader(), "com.google.gson.JsonObject"); this.constructor = getConstructor(clazz); this.keySetMethod = getMethod(clazz, "keySet"); this.getMethod = getMethod(clazz, "get", String.class); @@ -39,8 +38,4 @@ public void add(Object jsonObject, String key, Object element) { invoke(addMethod, jsonObject, key, element); } - public Class getWrappedClass() { - return clazz; - } - } diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JsonExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JsonExtension.java index e3aeaca9d..bce3a6806 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JsonExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JsonExtension.java @@ -68,11 +68,13 @@ private FormatterStep createStep() { public class GsonConfig { private int indentSpaces; private boolean sortByKeys; + private boolean escapeHtml; private String version; public GsonConfig() { this.indentSpaces = DEFAULT_INDENTATION; this.sortByKeys = false; + this.escapeHtml = false; this.version = DEFAULT_GSON_VERSION; addStep(createStep()); } @@ -89,6 +91,12 @@ public GsonConfig sortByKeys() { return this; } + public GsonConfig escapeHtml() { + this.escapeHtml = true; + replaceStep(createStep()); + return this; + } + public GsonConfig version(String version) { this.version = version; replaceStep(createStep()); @@ -96,7 +104,7 @@ public GsonConfig version(String version) { } private FormatterStep createStep() { - return GsonStep.create(indentSpaces, sortByKeys, version, provisioner()); + return GsonStep.create(indentSpaces, sortByKeys, escapeHtml, version, provisioner()); } } diff --git a/testlib/src/main/resources/json/cucumberJsonSampleGsonAfter.json b/testlib/src/main/resources/json/cucumberJsonSampleGsonAfter.json new file mode 100644 index 000000000..07e87bfd3 --- /dev/null +++ b/testlib/src/main/resources/json/cucumberJsonSampleGsonAfter.json @@ -0,0 +1,660 @@ +[ + { + "id": "account-holder-withdraws-cash", + "tags": [ + { + "name": "@featureTag", + "line": 1 + } + ], + "description": "This is description of the feature", + "name": "1st feature", + "keyword": "Feature", + "line": 2, + "elements": [ + { + "description": "Perfect background", + "name": "Activate Credit Card", + "keyword": "Background", + "line": 7, + "steps": [ + { + "result": { + "duration": 99107447000, + "status": "passed" + }, + "name": "I have a new credit card", + "keyword": "Given ", + "line": 8, + "match": { + "location": "ATMScenario.I_have_a_new_credit_card()" + }, + "embeddings": [ + { + "mime_type": "image/url", + "data": "" + }, + { + "data": "", + "media": { + "type": "text/plain" + } + } + ] + }, + { + "result": { + "duration": 9520000, + "status": "passed" + }, + "name": "My credit card is described as follow:", + "keyword": "And ", + "line": 9, + "match": { + "location": "ATMScenario.My_credit_card_is_described_as_follow" + }, + "doc_string": { + "content_type": "", + "line": 10, + "value": "{\n\"issuer\": {\n\"name\": \"Real Bank Inc.\",\n\"isn:\": \"RB55800093842N\"\n},\n\"card_number\": \"4896 0215 8478 6325\",\n\"holder\": \"A guy\"\n}" + } + }, + { + "result": { + "duration": 7040000, + "status": "passed" + }, + "name": "I confirm my pin number", + "keyword": "When ", + "line": 18, + "match": { + "location": "ATMScenario.I_confirm_my_pin_number()" + }, + "rows": [ + { + "cells": [ + "Müller", + "Deutschland" + ], + "line": 2 + }, + { + "cells": [ + "Nováková", + "Česko" + ], + "line": 3 + }, + { + "cells": [ + "Kovačević", + "Hrvatska" + ], + "line": 4 + }, + { + "cells": [ + "Παπαδόπουλος", + "Παπαδόπουλος" + ], + "line": 5 + }, + { + "cells": [ + "罗/羅", + "中國" + ], + "line": 6 + } + ] + }, + { + "result": { + "duration": 111111, + "status": "passed" + }, + "name": "the card should be activated", + "keyword": "Then ", + "line": 19, + "match": { + "location": "ATMScenario.the_card_should_be_activated()" + } + } + ], + "type": "background" + }, + { + "id": "account-holder-withdraws-cash;account-has-'sufficient-funds';;2", + "tags": [ + { + "name": "@fast", + "line": 21 + }, + { + "name": "@featureTag", + "line": 1 + }, + { + "name": "@checkout", + "line": 21 + } + ], + "description": "Account holder withdraws cash", + "name": "Account has ", + "keyword": "Scenario Outline", + "line": 33, + "steps": [ + { + "result": { + "duration": 17007000, + "status": "passed" + }, + "name": "the account balance is 100", + "keyword": "Given ", + "line": 23, + "match": { + "arguments": [ + { + "val": "100", + "offset": 23 + } + ], + "location": "ATMScenario.createAccount(int)" + } + }, + { + "result": { + "duration": 33444444, + "status": "passed" + }, + "name": "the card is valid", + "keyword": "And ", + "line": 24, + "match": { + "arguments": [ + { + "val": "", + "offset": 0 + } + ], + "location": "ATMScenario.createCreditCard()" + } + }, + { + "result": { + "duration": 44333333, + "status": "passed" + }, + "name": "100 is contained in the machine", + "keyword": "And ", + "line": 25, + "match": { + "arguments": [ + { + "val": "100", + "offset": 0 + } + ], + "location": "ATMScenario.createATM(int)" + }, + "matchedColumns": [ + 1 + ] + }, + { + "result": { + "duration": 11000001, + "status": "passed" + }, + "name": "the Account Holder requests 10, entering PIN 1234", + "keyword": "When ", + "line": 26, + "match": { + "arguments": [ + { + "val": "10", + "offset": 28 + }, + { + "val": "1234", + "offset": 45 + } + ], + "location": "ATMScenario.requestMoney(int)" + }, + "matchedColumns": [ + 2 + ] + }, + { + "result": { + "duration": 3220000, + "status": "passed" + }, + "name": "the ATM should dispense 10 monetary units", + "keyword": "Then ", + "line": 27, + "match": { + "arguments": [ + { + "val": "10", + "offset": 24 + }, + { + "val": "", + "offset": 0 + } + ], + "location": "ATMScenario.checkMoney(int)" + }, + "matchedColumns": [ + 3 + ] + }, + { + "result": { + "duration": 30000000, + "status": "passed" + }, + "name": "the account balance should be 90", + "keyword": "And ", + "line": 28, + "arguments": [ + { + "rows": [ + { + "cells": [ + "max", + "min" + ] + }, + { + "cells": [ + "20", + "3" + ] + } + ] + } + ], + "match": { + "location": "ATMScenario.checkBalance(int)" + }, + "matchedColumns": [ + 2 + ] + } + ], + "type": "scenario", + "after": [ + { + "result": { + "duration": 60744700, + "status": "passed", + "error_message": "Completed" + }, + "match": { + "location": "MachineFactory.timeout()" + } + } + ] + } + ], + "uri": "net/masterthought/example(s)/ATM:東京.feature" + }, + { + "id": "account-holder-withdraws-more-cash", + "description": "As an Account Holder\nI want to withdraw cash from an ATM,
so that I can get money when the bank is closed", + "name": "Second feature", + "keyword": "Feature", + "line": 1, + "elements": [ + { + "id": "account-holder-withdraws-more-cash;account-has-sufficient-funds;;2", + "tags": [ + { + "name": "@checkout", + "line": 101 + } + ], + "before": [ + { + "output": [ + "System version: beta3" + ], + "result": { + "duration": 10744700, + "status": "passed" + }, + "match": { + "location": "MachineFactory.findCashMachine()" + } + }, + { + "result": { + "duration": 1000001, + "status": "failed", + "error_message": " \n" + }, + "match": { + "location": "MachineFactory.wait()" + } + } + ], + "description": "Account holder withdraws more cash", + "name": "Account may not have sufficient funds", + "keyword": "Scenario Outline", + "line": 19, + "steps": [ + { + "result": { + "status": "undefined" + }, + "name": "the account balance is 100", + "keyword": "Given ", + "line": 7, + "match": { + "arguments": [ + { + "val": "100", + "offset": 23 + }, + {} + ] + }, + "matchedColumns": [ + 0 + ], + "before": [ + { + "embeddings": [ + { + "mime_type": "text/plain", + "data": "" + } + ], + "result": { + "duration": 410802047, + "status": "failed" + } + } + ] + }, + { + "result": { + "duration": 13000, + "status": "passed" + }, + "name": "the card is valid", + "keyword": "And ", + "line": 8, + "match": { + "arguments": [ + { + "val": "", + "offset": 17 + } + ], + "location": "ATMScenario.createCreditCard()" + }, + "after": [ + { + "result": { + "duration": 410802048, + "status": "passed" + }, + "match": { + "location": "StepHook.afterStep()" + } + } + ] + }, + { + "result": { + "duration": 36000, + "status": "passed" + }, + "name": "the machine contains 100", + "keyword": "And ", + "line": 9, + "match": { + "arguments": [ + { + "val": "100", + "offset": 21 + } + ], + "location": "ATMScenario.createATM(int)" + }, + "matchedColumns": [ + 1 + ] + }, + { + "result": { + "duration": 32000, + "status": "passed" + }, + "name": "the Account Holder requests 20", + "keyword": "When ", + "line": 10, + "match": { + "arguments": [ + { + "val": "20", + "offset": 28 + } + ], + "location": "ATMScenario.requestMoney(int)" + }, + "matchedColumns": [ + 2 + ] + }, + { + "result": { + "duration": 36000, + "status": "passed" + }, + "name": "the ATM should dispense 20", + "keyword": "Then ", + "line": 11, + "match": { + "arguments": [ + { + "val": "20", + "offset": 24 + } + ], + "location": "ATMScenario.checkMoney(int)" + }, + "matchedColumns": [ + 3 + ] + }, + { + "result": { + "duration": 1933000, + "status": "skipped", + "error_message": "java.lang.AssertionError: \nExpected: is <80>\n got: <90>\n\n\tat org.junit.Assert.assertThat(Assert.java:780)\n\tat org.junit.Assert.assertThat(Assert.java:738)\n\tat net.masterthought.example.ATMScenario.checkBalance(ATMScenario.java:69)\n\tat ✽.And the account balance should be 90(net/masterthought/example/ATMK.feature:12)\n" + }, + "name": "the account balance should be 90", + "keyword": "And ", + "line": 12, + "match": { + "arguments": [ + { + "val": "90", + "offset": 30 + } + ], + "location": "ATMScenario.checkBalance(int)" + }, + "matchedColumns": [ + 4 + ], + "embeddings": [ + { + "mime_type": "image/png", + "data": "", + "name": "Some PNG image" + }, + { + "mime_type": "image/jpeg", + "data": "" + }, + { + "mime_type": "text/plain", + "data": "" + }, + { + "mime_type": "text/html", + "data": "", + "name": "Some HTML embedding" + }, + { + "mime_type": "text/xml", + "data": "" + }, + { + "mime_type": "image/svg+xml", + "data": "" + }, + { + "mime_type": "js", + "data": "" + }, + { + "mime_type": "text/plain", + "data": "" + }, + { + "mime_type": "text/csv", + "data": "" + }, + { + "mime_type": "video/mp4", + "data": "" + } + ] + }, + { + "result": { + "status": "pending" + }, + "name": "the card should be returned", + "keyword": "And ", + "line": 13, + "match": { + "location": "ATMScenario.cardShouldBeReturned()" + }, + "embeddings": [ + { + "mime_type": "application/json", + "data": "" + } + ] + }, + { + "result": { + "status": "skipped" + }, + "name": "its not implemented", + "keyword": "And ", + "line": 14, + "match": { + "location": "ATMScenario.its_not_implemented()" + }, + "output": [ + [ + "Could not connect to the server @Rocky@" + ], + [ + "Could not connect to the server @Mike@" + ] + ] + }, + { + "result": { + "status": "failed" + }, + "name": "the card is valid", + "keyword": "And ", + "line": 15, + "match": { + "location": "ATMScenario.createCreditCard()" + }, + "output": [ + "Checkpoints", + 232 + ] + }, + { + "result": { + "duration": 90000000, + "status": "ambiguous" + }, + "name": "the card should be returned", + "keyword": "And ", + "line": 29, + "match": { + "location": "ATMScenario.cardShouldBeReturned()" + } + } + ], + "type": "scenario", + "after": [ + { + "result": { + "duration": 64700000, + "status": "undefined", + "error_message": "Undefined step" + }, + "match": { + "location": "any.error()" + }, + "embeddings": [ + { + "mime_type": "image/png", + "data": "" + } + ] + } + ] + }, + { + "id": "account-holder-withdraws-more-cash;clean-up", + "name": "Clean-up", + "keyword": "Scenario", + "line": 31, + "steps": [ + { + "result": { + "duration": 560000, + "status": "passed" + }, + "name": "Stream closing", + "keyword": "Given ", + "line": 32 + } + ], + "type": "scenario" + }, + { + "id": "undefined-result", + "name": "This step has no result...", + "keyword": "Scenario", + "line": 35, + "steps": [ + { + "name": " - even it should", + "keyword": "Given ", + "line": 36 + } + ], + "type": "scenario" + } + ], + "uri": "net/masterthought/example/ATMK.feature" + } +] diff --git a/testlib/src/main/resources/json/cucumberJsonSampleGsonBefore.json b/testlib/src/main/resources/json/cucumberJsonSampleGsonBefore.json new file mode 100644 index 000000000..8250630cf --- /dev/null +++ b/testlib/src/main/resources/json/cucumberJsonSampleGsonBefore.json @@ -0,0 +1,660 @@ +[ + { + "id": "account-holder-withdraws-cash", + "tags": [ + { + "name": "@featureTag", + "line": 1 + } + ], + "description": "This is description of the feature", + "name": "1st feature", + "keyword": "Feature", + "line": 2, + "elements": [ + { + "description": "Perfect background", + "name": "Activate Credit Card", + "keyword": "Background", + "line": 7, + "steps": [ + { + "result": { + "duration": 99107447000, + "status": "passed" + }, + "name": "I have a new credit card", + "keyword": "Given ", + "line": 8, + "match": { + "location": "ATMScenario.I_have_a_new_credit_card()" + }, + "embeddings": [ + { + "mime_type": "image/url", + "data": "" + }, + { + "data": "", + "media": { + "type": "text/plain" + } + } + ] + }, + { + "result": { + "duration": 9520000, + "status": "passed" + }, + "name": "My credit card is described as follow:", + "keyword": "And ", + "line": 9, + "match": { + "location": "ATMScenario.My_credit_card_is_described_as_follow" + }, + "doc_string": { + "content_type": "", + "line": 10, + "value": "{\n\"issuer\": {\n\"name\": \"Real Bank Inc.\",\n\"isn:\": \"RB55800093842N\"\n},\n\"card_number\": \"4896 0215 8478 6325\",\n\"holder\": \"A guy\"\n}" + } + }, + { + "result": { + "duration": 7040000, + "status": "passed" + }, + "name": "I confirm my pin number", + "keyword": "When ", + "line": 18, + "match": { + "location": "ATMScenario.I_confirm_my_pin_number()" + }, + "rows": [ + { + "cells": [ + "Müller", + "Deutschland" + ], + "line": 2 + }, + { + "cells": [ + "Nováková", + "Česko" + ], + "line": 3 + }, + { + "cells": [ + "Kovačević", + "Hrvatska" + ], + "line": 4 + }, + { + "cells": [ + "Παπαδόπουλος", + "Παπαδόπουλος" + ], + "line": 5 + }, + { + "cells": [ + "罗/羅", + "中國" + ], + "line": 6 + } + ] + }, + { + "result": { + "duration": 111111, + "status": "passed" + }, + "name": "the card should be activated", + "keyword": "Then ", + "line": 19, + "match": { + "location": "ATMScenario.the_card_should_be_activated()" + } + } + ], + "type": "background" + }, + { + "id": "account-holder-withdraws-cash;account-has-\u0027sufficient-funds\u0027;;2", + "tags": [ + { + "name": "@fast", + "line": 21 + }, + { + "name": "@featureTag", + "line": 1 + }, + { + "name": "@checkout", + "line": 21 + } + ], + "description": "Account holder withdraws cash", + "name": "Account has ", + "keyword": "Scenario Outline", + "line": 33, + "steps": [ + { + "result": { + "duration": 17007000, + "status": "passed" + }, + "name": "the account balance is 100", + "keyword": "Given ", + "line": 23, + "match": { + "arguments": [ + { + "val": "100", + "offset": 23 + } + ], + "location": "ATMScenario.createAccount(int)" + } + }, + { + "result": { + "duration": 33444444, + "status": "passed" + }, + "name": "the card is valid", + "keyword": "And ", + "line": 24, + "match": { + "arguments": [ + { + "val": "", + "offset": 0 + } + ], + "location": "ATMScenario.createCreditCard()" + } + }, + { + "result": { + "duration": 44333333, + "status": "passed" + }, + "name": "100 is contained in the machine", + "keyword": "And ", + "line": 25, + "match": { + "arguments": [ + { + "val": "100", + "offset": 0 + } + ], + "location": "ATMScenario.createATM(int)" + }, + "matchedColumns": [ + 1 + ] + }, + { + "result": { + "duration": 11000001, + "status": "passed" + }, + "name": "the Account Holder requests 10, entering PIN 1234", + "keyword": "When ", + "line": 26, + "match": { + "arguments": [ + { + "val": "10", + "offset": 28 + }, + { + "val": "1234", + "offset": 45 + } + ], + "location": "ATMScenario.requestMoney(int)" + }, + "matchedColumns": [ + 2 + ] + }, + { + "result": { + "duration": 3220000, + "status": "passed" + }, + "name": "the ATM should dispense 10 monetary units", + "keyword": "Then ", + "line": 27, + "match": { + "arguments": [ + { + "val": "10", + "offset": 24 + }, + { + "val": "", + "offset": 0 + } + ], + "location": "ATMScenario.checkMoney(int)" + }, + "matchedColumns": [ + 3 + ] + }, + { + "result": { + "duration": 30000000, + "status": "passed" + }, + "name": "the account balance should be 90", + "keyword": "And ", + "line": 28, + "arguments": [ + { + "rows": [ + { + "cells": [ + "max", + "min" + ] + }, + { + "cells": [ + "20", + "3" + ] + } + ] + } + ], + "match": { + "location": "ATMScenario.checkBalance(int)" + }, + "matchedColumns": [ + 2 + ] + } + ], + "type": "scenario", + "after": [ + { + "result": { + "duration": 60744700, + "status": "passed", + "error_message": "Completed" + }, + "match": { + "location": "MachineFactory.timeout()" + } + } + ] + } + ], + "uri": "net/masterthought/example(s)/ATM:東京.feature" + }, + { + "id": "account-holder-withdraws-more-cash", + "description": "As an Account Holder\nI want to withdraw cash from an ATM,
so that I can get money when the bank is closed", + "name": "Second feature", + "keyword": "Feature", + "line": 1, + "elements": [ + { + "id": "account-holder-withdraws-more-cash;account-has-sufficient-funds;;2", + "tags": [ + { + "name": "@checkout", + "line": 101 + } + ], + "before": [ + { + "output": [ + "System version: beta3" + ], + "result": { + "duration": 10744700, + "status": "passed" + }, + "match": { + "location": "MachineFactory.findCashMachine()" + } + }, + { + "result": { + "duration": 1000001, + "status": "failed", + "error_message": " \n" + }, + "match": { + "location": "MachineFactory.wait()" + } + } + ], + "description": "Account holder withdraws more cash", + "name": "Account may not have sufficient funds", + "keyword": "Scenario Outline", + "line": 19, + "steps": [ + { + "result": { + "status": "undefined" + }, + "name": "the account balance is 100", + "keyword": "Given ", + "line": 7, + "match": { + "arguments": [ + { + "val": "100", + "offset": 23 + }, + {} + ] + }, + "matchedColumns": [ + 0 + ], + "before": [ + { + "embeddings": [ + { + "mime_type": "text/plain", + "data": "" + } + ], + "result": { + "duration": 410802047, + "status": "failed" + } + } + ] + }, + { + "result": { + "duration": 13000, + "status": "passed" + }, + "name": "the card is valid", + "keyword": "And ", + "line": 8, + "match": { + "arguments": [ + { + "val": "", + "offset": 17 + } + ], + "location": "ATMScenario.createCreditCard()" + }, + "after": [ + { + "result": { + "duration": 410802048, + "status": "passed" + }, + "match": { + "location": "StepHook.afterStep()" + } + } + ] + }, + { + "result": { + "duration": 36000, + "status": "passed" + }, + "name": "the machine contains 100", + "keyword": "And ", + "line": 9, + "match": { + "arguments": [ + { + "val": "100", + "offset": 21 + } + ], + "location": "ATMScenario.createATM(int)" + }, + "matchedColumns": [ + 1 + ] + }, + { + "result": { + "duration": 32000, + "status": "passed" + }, + "name": "the Account Holder requests 20", + "keyword": "When ", + "line": 10, + "match": { + "arguments": [ + { + "val": "20", + "offset": 28 + } + ], + "location": "ATMScenario.requestMoney(int)" + }, + "matchedColumns": [ + 2 + ] + }, + { + "result": { + "duration": 36000, + "status": "passed" + }, + "name": "the ATM should dispense 20", + "keyword": "Then ", + "line": 11, + "match": { + "arguments": [ + { + "val": "20", + "offset": 24 + } + ], + "location": "ATMScenario.checkMoney(int)" + }, + "matchedColumns": [ + 3 + ] + }, + { + "result": { + "duration": 1933000, + "status": "skipped", + "error_message": "java.lang.AssertionError: \nExpected: is \u003c80\u003e\n got: \u003c90\u003e\n\n\tat org.junit.Assert.assertThat(Assert.java:780)\n\tat org.junit.Assert.assertThat(Assert.java:738)\n\tat net.masterthought.example.ATMScenario.checkBalance(ATMScenario.java:69)\n\tat ✽.And the account balance should be 90(net/masterthought/example/ATMK.feature:12)\n" + }, + "name": "the account balance should be 90", + "keyword": "And ", + "line": 12, + "match": { + "arguments": [ + { + "val": "90", + "offset": 30 + } + ], + "location": "ATMScenario.checkBalance(int)" + }, + "matchedColumns": [ + 4 + ], + "embeddings": [ + { + "mime_type": "image/png", + "data": "", + "name": "Some PNG image" + }, + { + "mime_type": "image/jpeg", + "data": "" + }, + { + "mime_type": "text/plain", + "data": "" + }, + { + "mime_type": "text/html", + "data": "", + "name": "Some HTML embedding" + }, + { + "mime_type": "text/xml", + "data": "" + }, + { + "mime_type": "image/svg+xml", + "data": "" + }, + { + "mime_type": "js", + "data": "" + }, + { + "mime_type": "text/plain", + "data": "" + }, + { + "mime_type": "text/csv", + "data": "" + }, + { + "mime_type": "video/mp4", + "data": "" + } + ] + }, + { + "result": { + "status": "pending" + }, + "name": "the card should be returned", + "keyword": "And ", + "line": 13, + "match": { + "location": "ATMScenario.cardShouldBeReturned()" + }, + "embeddings": [ + { + "mime_type": "application/json", + "data": "" + } + ] + }, + { + "result": { + "status": "skipped" + }, + "name": "its not implemented", + "keyword": "And ", + "line": 14, + "match": { + "location": "ATMScenario.its_not_implemented()" + }, + "output": [ + [ + "Could not connect to the server @Rocky@" + ], + [ + "Could not connect to the server @Mike@" + ] + ] + }, + { + "result": { + "status": "failed" + }, + "name": "the card is valid", + "keyword": "And ", + "line": 15, + "match": { + "location": "ATMScenario.createCreditCard()" + }, + "output": [ + "Checkpoints", + 232 + ] + }, + { + "result": { + "duration": 90000000, + "status": "ambiguous" + }, + "name": "the card should be returned", + "keyword": "And ", + "line": 29, + "match": { + "location": "ATMScenario.cardShouldBeReturned()" + } + } + ], + "type": "scenario", + "after": [ + { + "result": { + "duration": 64700000, + "status": "undefined", + "error_message": "Undefined step" + }, + "match": { + "location": "any.error()" + }, + "embeddings": [ + { + "mime_type": "image/png", + "data": "" + } + ] + } + ] + }, + { + "id": "account-holder-withdraws-more-cash;clean-up", + "name": "Clean-up", + "keyword": "Scenario", + "line": 31, + "steps": [ + { + "result": { + "duration": 560000, + "status": "passed" + }, + "name": "Stream closing", + "keyword": "Given ", + "line": 32 + } + ], + "type": "scenario" + }, + { + "id": "undefined-result", + "name": "This step has no result...", + "keyword": "Scenario", + "line": 35, + "steps": [ + { + "name": " - even it should", + "keyword": "Given ", + "line": 36 + } + ], + "type": "scenario" + } + ], + "uri": "net/masterthought/example/ATMK.feature" + } +] diff --git a/testlib/src/main/resources/json/escapeHtmlGsonAfter.json b/testlib/src/main/resources/json/escapeHtmlGsonAfter.json new file mode 100644 index 000000000..f83692da2 --- /dev/null +++ b/testlib/src/main/resources/json/escapeHtmlGsonAfter.json @@ -0,0 +1,4 @@ +{ + "escapedHtml": "\u003ca href\u003d\u0027https://www.google.com/search?q\u003ddiffplug-spotless\u0026tbm\u003disch\u0027\u003eSee some pictures!\u003c\\a\u003e", + "rawHtml": "\u003ca href\u003d\u0027https://www.google.com/search?q\u003ddiffplug-spotless\u0026tbm\u003disch\u0027\u003eSee some pictures!\u003c\\a\u003e" +} diff --git a/testlib/src/main/resources/json/escapeHtmlGsonAfterDisabled.json b/testlib/src/main/resources/json/escapeHtmlGsonAfterDisabled.json new file mode 100644 index 000000000..fe033341f --- /dev/null +++ b/testlib/src/main/resources/json/escapeHtmlGsonAfterDisabled.json @@ -0,0 +1,4 @@ +{ + "escapedHtml": "See some pictures!<\\a>", + "rawHtml": "See some pictures!<\\a>" +} diff --git a/testlib/src/main/resources/json/escapeHtmlGsonBefore.json b/testlib/src/main/resources/json/escapeHtmlGsonBefore.json new file mode 100644 index 000000000..400e862d9 --- /dev/null +++ b/testlib/src/main/resources/json/escapeHtmlGsonBefore.json @@ -0,0 +1,4 @@ +{ + "escapedHtml": "\u003ca href\u003d\u0027https://www.google.com/search?q\u003ddiffplug-spotless\u0026tbm\u003disch\u0027\u003eSee some pictures!\u003c\\a\u003e", + "rawHtml": "See some pictures!<\\a>" +} diff --git a/testlib/src/main/resources/json/objectWithNullGsonAfter.json b/testlib/src/main/resources/json/objectWithNullGsonAfter.json new file mode 100644 index 000000000..368632dd8 --- /dev/null +++ b/testlib/src/main/resources/json/objectWithNullGsonAfter.json @@ -0,0 +1,4 @@ +{ + "value": null, + "another": 1 +} diff --git a/testlib/src/main/resources/json/objectWithNullGsonBefore.json b/testlib/src/main/resources/json/objectWithNullGsonBefore.json new file mode 100644 index 000000000..f68567c97 --- /dev/null +++ b/testlib/src/main/resources/json/objectWithNullGsonBefore.json @@ -0,0 +1,4 @@ +{ +"value": null, +"another": 1 +} diff --git a/testlib/src/main/resources/json/sortByKeysAfter.json b/testlib/src/main/resources/json/sortByKeysAfter.json new file mode 100644 index 000000000..c4a48de2f --- /dev/null +++ b/testlib/src/main/resources/json/sortByKeysAfter.json @@ -0,0 +1,19 @@ +{ + "A": 1, + "X": 2, + "_arraysNotSorted": [ + 3, + 2, + 1 + ], + "a": 3, + "c": 4, + "x": 5, + "z": { + "A": 1, + "X": 2, + "a": 3, + "c": 4, + "x": 5 + } +} diff --git a/testlib/src/main/resources/json/sortByKeysAfterDisabled.json b/testlib/src/main/resources/json/sortByKeysAfterDisabled.json new file mode 100644 index 000000000..de7462bb9 --- /dev/null +++ b/testlib/src/main/resources/json/sortByKeysAfterDisabled.json @@ -0,0 +1,19 @@ +{ + "z": { + "c": 4, + "x": 5, + "X": 2, + "A": 1, + "a": 3 + }, + "c": 4, + "x": 5, + "X": 2, + "A": 1, + "a": 3, + "_arraysNotSorted": [ + 3, + 2, + 1 + ] +} diff --git a/testlib/src/main/resources/json/sortByKeysBefore.json b/testlib/src/main/resources/json/sortByKeysBefore.json new file mode 100644 index 000000000..de7462bb9 --- /dev/null +++ b/testlib/src/main/resources/json/sortByKeysBefore.json @@ -0,0 +1,19 @@ +{ + "z": { + "c": 4, + "x": 5, + "X": 2, + "A": 1, + "a": 3 + }, + "c": 4, + "x": 5, + "X": 2, + "A": 1, + "a": 3, + "_arraysNotSorted": [ + 3, + 2, + 1 + ] +} diff --git a/testlib/src/test/java/com/diffplug/spotless/json/GsonStepTest.java b/testlib/src/test/java/com/diffplug/spotless/json/GsonStepTest.java new file mode 100644 index 000000000..80fde970c --- /dev/null +++ b/testlib/src/test/java/com/diffplug/spotless/json/GsonStepTest.java @@ -0,0 +1,85 @@ +package com.diffplug.spotless.json; + +import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.Provisioner; + +import com.diffplug.spotless.StepHarness; +import com.diffplug.spotless.TestProvisioner; +import com.diffplug.spotless.json.gson.GsonStep; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class GsonStepTest extends JsonFormatterStepCommonTests { + + private static final String DEFAULT_VERSION = "2.8.9"; + + @Test + void handlesComplexNestedObject() throws Exception { + doWithResource("cucumberJsonSampleGson"); + } + + @Test + void handlesObjectWithNull() throws Exception { + doWithResource("objectWithNullGson"); + } + + @Test + void handlesInvalidJson() { + assertThatThrownBy(() -> doWithResource("invalidJson")) + .isInstanceOf(AssertionError.class) + .hasMessage("Unable to format JSON") + .hasRootCauseMessage("End of input at line 3 column 1 path $.a"); + } + + @Test + void handlesNotJson() { + assertThatThrownBy(() -> doWithResource("notJson")) + .isInstanceOf(AssertionError.class) + .hasMessage("Unable to format JSON") + .hasNoCause(); + } + + @Test + void handlesSortingWhenSortByKeyEnabled() throws Exception { + FormatterStep step = GsonStep.create(INDENT, true, false, DEFAULT_VERSION, TestProvisioner.mavenCentral()); + StepHarness stepHarness = StepHarness.forStep(step); + stepHarness.testResource("json/sortByKeysBefore.json", "json/sortByKeysAfter.json"); + } + + @Test + void doesNoSortingWhenSortByKeyDisabled() throws Exception { + FormatterStep step = GsonStep.create(INDENT, false, false, DEFAULT_VERSION, TestProvisioner.mavenCentral()); + StepHarness.forStep(step) + .testResource("json/sortByKeysBefore.json", "json/sortByKeysAfterDisabled.json"); + } + + @Test + void handlesHtmlEscapeWhenEnabled() throws Exception { + FormatterStep step = GsonStep.create(INDENT, false, true, DEFAULT_VERSION, TestProvisioner.mavenCentral()); + StepHarness.forStep(step) + .testResource("json/escapeHtmlGsonBefore.json", "json/escapeHtmlGsonAfter.json"); + } + + @Test + void writesRawHtmlWhenHtmlEscapeDisabled() throws Exception { + FormatterStep step = GsonStep.create(INDENT, false, false, DEFAULT_VERSION, TestProvisioner.mavenCentral()); + StepHarness.forStep(step) + .testResource("json/escapeHtmlGsonBefore.json", "json/escapeHtmlGsonAfterDisabled.json"); + } + + @Test + void handlesVersionIncompatibility() { + FormatterStep step = GsonStep.create(INDENT, false, false, "1.7", TestProvisioner.mavenCentral()); + StepHarness stepHarness = StepHarness.forStep(step); + assertThatThrownBy(() -> stepHarness.testResource("json/cucumberJsonSampleGsonBefore.json", "json/cucumberJsonSampleGsonAfter.json")) + .isInstanceOf(IllegalStateException.class) + .hasMessage("There was a problem interacting with Gson; maybe you set an incompatible version?"); + } + + @Override + protected FormatterStep createFormatterStep(int indent, Provisioner provisioner) { + return GsonStep.create(indent, false, false, DEFAULT_VERSION, provisioner); + } +} diff --git a/testlib/src/test/java/com/diffplug/spotless/json/JsonFormatterStepCommonTests.java b/testlib/src/test/java/com/diffplug/spotless/json/JsonFormatterStepCommonTests.java new file mode 100644 index 000000000..dd8d9ba69 --- /dev/null +++ b/testlib/src/test/java/com/diffplug/spotless/json/JsonFormatterStepCommonTests.java @@ -0,0 +1,89 @@ +package com.diffplug.spotless.json; + +import com.diffplug.spotless.*; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public abstract class JsonFormatterStepCommonTests { + + protected static final int INDENT = 4; + + @Test + void cannotProvideNullProvisioner() { + assertThatThrownBy(() -> createFormatterStep(INDENT, null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("provisioner cannot be null"); + } + + @Test + void handlesNestedObject() throws Exception { + doWithResource("nestedObject"); + } + + @Test + void handlesSingletonArray() throws Exception { + doWithResource("singletonArray"); + } + + @Test + void handlesEmptyFile() throws Exception { + doWithResource("empty"); + } + + @Test + void canSetCustomIndentationLevel() throws Exception { + FormatterStep step = createFormatterStep(6, TestProvisioner.mavenCentral()); + StepHarness stepHarness = StepHarness.forStep(step); + + String before = "json/singletonArrayBefore.json"; + String after = "json/singletonArrayAfter6Spaces.json"; + stepHarness.testResource(before, after); + } + + @Test + void canSetIndentationLevelTo0() throws Exception { + FormatterStep step = createFormatterStep(0, TestProvisioner.mavenCentral()); + StepHarness stepHarness = StepHarness.forStep(step); + + String before = "json/singletonArrayBefore.json"; + String after = "json/singletonArrayAfter0Spaces.json"; + stepHarness.testResource(before, after); + } + + @Test + void equality() { + new SerializableEqualityTester() { + int spaces = 0; + + @Override + protected void setupTest(API api) { + // no changes, are the same + api.areDifferentThan(); + + // with different spacing + spaces = 1; + api.areDifferentThan(); + } + + @Override + protected FormatterStep create() { + return createFormatterStep(spaces, TestProvisioner.mavenCentral()); + } + }.testEquals(); + } + + protected abstract FormatterStep createFormatterStep(int indent, Provisioner provisioner); + + protected StepHarness getStepHarness() { + return StepHarness.forStep(createFormatterStep(INDENT, TestProvisioner.mavenCentral())); + } + + protected void doWithResource(String name) throws Exception { + String before = String.format("json/%sBefore.json", name); + String after = String.format("json/%sAfter.json", name); + getStepHarness().testResource(before, after); + } + +} diff --git a/testlib/src/test/java/com/diffplug/spotless/json/JsonSimpleStepTest.java b/testlib/src/test/java/com/diffplug/spotless/json/JsonSimpleStepTest.java index 408574459..a8b166fa2 100644 --- a/testlib/src/test/java/com/diffplug/spotless/json/JsonSimpleStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/json/JsonSimpleStepTest.java @@ -17,121 +17,50 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; -import org.junit.jupiter.api.Test; - -import com.diffplug.spotless.FormatterStep; -import com.diffplug.spotless.SerializableEqualityTester; -import com.diffplug.spotless.StepHarness; -import com.diffplug.spotless.TestProvisioner; - -class JsonSimpleStepTest { - - private static final int INDENT = 4; +import com.diffplug.spotless.*; - private final FormatterStep step = JsonSimpleStep.create(INDENT, TestProvisioner.mavenCentral()); - private final StepHarness stepHarness = StepHarness.forStep(step); +import org.junit.jupiter.api.Test; - @Test - void cannotProvidedNullProvisioner() { - assertThatThrownBy(() -> JsonSimpleStep.create(INDENT, null)).isInstanceOf(NullPointerException.class).hasMessage("provisioner cannot be null"); - } +class JsonSimpleStepTest extends JsonFormatterStepCommonTests { @Test void handlesSingletonObject() throws Exception { - doWithResource(stepHarness, "singletonObject"); + doWithResource("singletonObject"); } @Test void handlesSingletonObjectWithArray() throws Exception { - doWithResource(stepHarness, "singletonObjectWithArray"); - } - - @Test - void handlesNestedObject() throws Exception { - doWithResource(stepHarness, "nestedObject"); - } - - @Test - void handlesSingletonArray() throws Exception { - doWithResource(stepHarness, "singletonArray"); - } - - @Test - void handlesEmptyFile() throws Exception { - doWithResource(stepHarness, "empty"); + doWithResource("singletonObjectWithArray"); } @Test void handlesComplexNestedObject() throws Exception { - doWithResource(stepHarness, "cucumberJsonSample"); + doWithResource("cucumberJsonSample"); } @Test void handlesObjectWithNull() throws Exception { - doWithResource(stepHarness, "objectWithNull"); + doWithResource("objectWithNull"); } @Test void handlesInvalidJson() { - assertThatThrownBy(() -> doWithResource(stepHarness, "invalidJson")) - .isInstanceOf(AssertionError.class) - .hasMessage("Unable to format JSON") - .hasRootCauseMessage("Expected a ',' or '}' at 9 [character 0 line 3]"); + assertThatThrownBy(() -> doWithResource("invalidJson")) + .isInstanceOf(AssertionError.class) + .hasMessage("Unable to format JSON") + .hasRootCauseMessage("Expected a ',' or '}' at 9 [character 0 line 3]"); } @Test void handlesNotJson() { - assertThatThrownBy(() -> doWithResource(stepHarness, "notJson")) - .isInstanceOf(AssertionError.class) - .hasMessage("Unable to determine JSON type, expected a '{' or '[' but found '#'") - .hasNoCause(); - } - - @Test - void canSetCustomIndentationLevel() throws Exception { - FormatterStep step = JsonSimpleStep.create(6, TestProvisioner.mavenCentral()); - StepHarness stepHarness = StepHarness.forStep(step); - - String before = "json/singletonArrayBefore.json"; - String after = "json/singletonArrayAfter6Spaces.json"; - stepHarness.testResource(before, after); - } - - @Test - void canSetIndentationLevelTo0() throws Exception { - FormatterStep step = JsonSimpleStep.create(0, TestProvisioner.mavenCentral()); - StepHarness stepHarness = StepHarness.forStep(step); - - String before = "json/singletonArrayBefore.json"; - String after = "json/singletonArrayAfter0Spaces.json"; - stepHarness.testResource(before, after); - } - - @Test - void equality() { - new SerializableEqualityTester() { - int spaces = 0; - - @Override - protected void setupTest(API api) { - // no changes, are the same - api.areDifferentThan(); - - // with different spacing - spaces = 1; - api.areDifferentThan(); - } - - @Override - protected FormatterStep create() { - return JsonSimpleStep.create(spaces, TestProvisioner.mavenCentral()); - } - }.testEquals(); + assertThatThrownBy(() -> doWithResource("notJson")) + .isInstanceOf(AssertionError.class) + .hasMessage("Unable to determine JSON type, expected a '{' or '[' but found '#'") + .hasNoCause(); } - private static void doWithResource(StepHarness stepHarness, String name) throws Exception { - String before = String.format("json/%sBefore.json", name); - String after = String.format("json/%sAfter.json", name); - stepHarness.testResource(before, after); + @Override + protected FormatterStep createFormatterStep(int indent, Provisioner provisioner) { + return JsonSimpleStep.create(indent, provisioner); } } From 7f15f16f4c00fdd3eabd4f1e79a9cd99a0f6fb32 Mon Sep 17 00:00:00 2001 From: Abel Keszei Date: Sat, 12 Feb 2022 18:24:11 +0100 Subject: [PATCH 04/12] add escapeHtml to README --- plugin-gradle/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index b3821d527..7a6089850 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -626,11 +626,19 @@ spotless { gson() .indentWithSpaces(6) // optional: specify the number of spaces to use .sortByKeys() // optional: sort JSON by its keys + .escapeHtml() // optional: escape HTML in values .version('2.8.1') // optional: specify version } } ``` +Notes: +* There's no option in Gson to leave HTML as-is (i.e. escaped HTML would remain escaped, raw would remain raw). Either +all HTML characters are written escaped or none. Set `escapeHtml` if you prefer the former. +* `sortByKeys` will apply lexicographic order on the keys of the input JSON. See the +[javadoc of String](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html#compareTo(java.lang.String)) +for details. + ## Prettier From 3835b5d8904c0e88144c1ec8800902df49e8b494 Mon Sep 17 00:00:00 2001 From: Abel Keszei Date: Sat, 12 Feb 2022 18:59:03 +0100 Subject: [PATCH 05/12] update JsonExtension tests --- .../gradle/spotless/JsonExtensionTest.java | 83 ++++++++++++++++++- 1 file changed, 81 insertions(+), 2 deletions(-) diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JsonExtensionTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JsonExtensionTest.java index edcd4b428..09ff70673 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JsonExtensionTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JsonExtensionTest.java @@ -21,7 +21,7 @@ class JsonExtensionTest extends GradleIntegrationHarness { @Test - void defaultFormatting() throws IOException { + void simpleDefaultFormatting() throws IOException { setFile("build.gradle").toLines( "plugins {", " id 'java'", @@ -42,7 +42,7 @@ void defaultFormatting() throws IOException { } @Test - void formattingWithCustomNumberOfSpaces() throws IOException { + void simpleFormattingWithCustomNumberOfSpaces() throws IOException { setFile("build.gradle").toLines( "plugins {", " id 'java'", @@ -59,4 +59,83 @@ void formattingWithCustomNumberOfSpaces() throws IOException { gradleRunner().withArguments("spotlessApply").build(); assertFile("src/main/resources/example.json").sameAsResource("json/singletonArrayAfter6Spaces.json"); } + + @Test + void gsonDefaultFormatting() throws IOException { + setFile("build.gradle").toLines( + "plugins {", + " id 'java'", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " json {", + " target 'examples/**/*.json'", + " gson()", + "}", + "}"); + setFile("src/main/resources/example.json").toResource("json/nestedObjectBefore.json"); + setFile("examples/main/resources/example.json").toResource("json/nestedObjectBefore.json"); + gradleRunner().withArguments("spotlessApply").build(); + assertFile("src/main/resources/example.json").sameAsResource("json/nestedObjectBefore.json"); + assertFile("examples/main/resources/example.json").sameAsResource("json/nestedObjectAfter.json"); + } + + @Test + void gsonFormattingWithCustomNumberOfSpaces() throws IOException { + setFile("build.gradle").toLines( + "plugins {", + " id 'java'", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " json {", + " target 'src/**/*.json'", + " gson().indentWithSpaces(6)", + "}", + "}"); + setFile("src/main/resources/example.json").toResource("json/singletonArrayBefore.json"); + gradleRunner().withArguments("spotlessApply").build(); + assertFile("src/main/resources/example.json").sameAsResource("json/singletonArrayAfter6Spaces.json"); + } + + @Test + void gsonFormattingWithSortingByKeys() throws IOException { + setFile("build.gradle").toLines( + "plugins {", + " id 'java'", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " json {", + " target 'src/**/*.json'", + " gson().sortByKeys()", + "}", + "}"); + setFile("src/main/resources/example.json").toResource("json/sortByKeysBefore.json"); + gradleRunner().withArguments("spotlessApply").build(); + assertFile("src/main/resources/example.json").sameAsResource("json/sortByKeysAfter.json"); + } + + @Test + void gsonFormattingWithHtmlEscape() throws IOException { + setFile("build.gradle").toLines( + "plugins {", + " id 'java'", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " json {", + " target 'src/**/*.json'", + " gson().escapeHtml()", + "}", + "}"); + setFile("src/main/resources/example.json").toResource("json/escapeHtmlGsonBefore.json"); + gradleRunner().withArguments("spotlessApply").build(); + assertFile("src/main/resources/example.json").sameAsResource("json/escapeHtmlGsonAfter.json"); + } + } From b42a8cd392d48b823523a6d02acfc30717c5efb7 Mon Sep 17 00:00:00 2001 From: Abel Keszei Date: Sat, 12 Feb 2022 19:00:30 +0100 Subject: [PATCH 06/12] apply format --- .../json/gson/GsonBuilderWrapper.java | 15 ++++ .../diffplug/spotless/json/gson/GsonStep.java | 39 +++++--- .../spotless/json/gson/GsonWrapper.java | 19 +++- .../spotless/json/gson/GsonWrapperBase.java | 15 ++++ .../json/gson/JsonElementWrapper.java | 21 ++++- .../spotless/json/gson/JsonObjectWrapper.java | 19 +++- .../spotless/json/gson/JsonWriterWrapper.java | 19 +++- .../gradle/spotless/JsonExtension.java | 2 +- .../gradle/spotless/JsonExtensionTest.java | 90 +++++++++---------- .../diffplug/spotless/json/GsonStepTest.java | 46 ++++++---- .../json/JsonFormatterStepCommonTests.java | 23 ++++- .../spotless/json/JsonSimpleStepTest.java | 18 ++-- 12 files changed, 229 insertions(+), 97 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/json/gson/GsonBuilderWrapper.java b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonBuilderWrapper.java index 1693a5f9d..365e27fb4 100644 --- a/lib/src/main/java/com/diffplug/spotless/json/gson/GsonBuilderWrapper.java +++ b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonBuilderWrapper.java @@ -1,3 +1,18 @@ +/* + * Copyright 2022 DiffPlug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.diffplug.spotless.json.gson; import java.lang.reflect.Constructor; diff --git a/lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java index 40ca84eeb..06519ae39 100644 --- a/lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java +++ b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java @@ -1,16 +1,31 @@ +/* + * Copyright 2022 DiffPlug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.diffplug.spotless.json.gson; -import com.diffplug.spotless.FormatterFunc; -import com.diffplug.spotless.FormatterStep; -import com.diffplug.spotless.JarState; -import com.diffplug.spotless.Provisioner; - import java.io.IOException; import java.io.Serializable; import java.io.StringWriter; import java.util.Collections; import java.util.Objects; +import com.diffplug.spotless.FormatterFunc; +import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.JarState; +import com.diffplug.spotless.Provisioner; + public class GsonStep { private static final String MAVEN_COORDINATES = "com.google.code.gson:gson"; @@ -73,13 +88,13 @@ FormatterFunc toFormatter() { private Object sortByKeys(JsonObjectWrapper jsonObjectWrapper, JsonElementWrapper jsonElementWrapper, Object jsonObject) { Object result = jsonObjectWrapper.createJsonObject(); jsonObjectWrapper.keySet(jsonObject).stream().sorted() - .forEach(key -> { - Object element = jsonObjectWrapper.get(jsonObject, key); - if (jsonElementWrapper.isJsonObject(element)) { - element = sortByKeys(jsonObjectWrapper, jsonElementWrapper, element); - } - jsonObjectWrapper.add(result, key, element); - }); + .forEach(key -> { + Object element = jsonObjectWrapper.get(jsonObject, key); + if (jsonElementWrapper.isJsonObject(element)) { + element = sortByKeys(jsonObjectWrapper, jsonElementWrapper, element); + } + jsonObjectWrapper.add(result, key, element); + }); return result; } diff --git a/lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapper.java b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapper.java index 53e765fa0..fabb25796 100644 --- a/lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapper.java +++ b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapper.java @@ -1,10 +1,25 @@ +/* + * Copyright 2022 DiffPlug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.diffplug.spotless.json.gson; -import com.diffplug.spotless.JarState; - import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import com.diffplug.spotless.JarState; + public class GsonWrapper extends GsonWrapperBase { private final Constructor constructor; diff --git a/lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapperBase.java b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapperBase.java index 31e22d53d..3438c6062 100644 --- a/lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapperBase.java +++ b/lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapperBase.java @@ -1,3 +1,18 @@ +/* + * Copyright 2022 DiffPlug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.diffplug.spotless.json.gson; import java.lang.reflect.Constructor; diff --git a/lib/src/main/java/com/diffplug/spotless/json/gson/JsonElementWrapper.java b/lib/src/main/java/com/diffplug/spotless/json/gson/JsonElementWrapper.java index a45225b41..cbb04a411 100644 --- a/lib/src/main/java/com/diffplug/spotless/json/gson/JsonElementWrapper.java +++ b/lib/src/main/java/com/diffplug/spotless/json/gson/JsonElementWrapper.java @@ -1,10 +1,23 @@ +/* + * Copyright 2022 DiffPlug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.diffplug.spotless.json.gson; -import com.diffplug.spotless.JarState; - -import java.lang.reflect.Constructor; import java.lang.reflect.Method; -import java.util.Set; + +import com.diffplug.spotless.JarState; public class JsonElementWrapper extends GsonWrapperBase { diff --git a/lib/src/main/java/com/diffplug/spotless/json/gson/JsonObjectWrapper.java b/lib/src/main/java/com/diffplug/spotless/json/gson/JsonObjectWrapper.java index 5485ce2ff..0e2d48cf7 100644 --- a/lib/src/main/java/com/diffplug/spotless/json/gson/JsonObjectWrapper.java +++ b/lib/src/main/java/com/diffplug/spotless/json/gson/JsonObjectWrapper.java @@ -1,11 +1,26 @@ +/* + * Copyright 2022 DiffPlug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.diffplug.spotless.json.gson; -import com.diffplug.spotless.JarState; - import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Set; +import com.diffplug.spotless.JarState; + public class JsonObjectWrapper extends GsonWrapperBase { private final Constructor constructor; diff --git a/lib/src/main/java/com/diffplug/spotless/json/gson/JsonWriterWrapper.java b/lib/src/main/java/com/diffplug/spotless/json/gson/JsonWriterWrapper.java index 580e075e7..55ce4c095 100644 --- a/lib/src/main/java/com/diffplug/spotless/json/gson/JsonWriterWrapper.java +++ b/lib/src/main/java/com/diffplug/spotless/json/gson/JsonWriterWrapper.java @@ -1,11 +1,26 @@ +/* + * Copyright 2022 DiffPlug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.diffplug.spotless.json.gson; -import com.diffplug.spotless.JarState; - import java.io.Writer; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import com.diffplug.spotless.JarState; + public class JsonWriterWrapper extends GsonWrapperBase { private final Class clazz; diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JsonExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JsonExtension.java index bce3a6806..25182fa4b 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JsonExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JsonExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 DiffPlug + * Copyright 2016-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JsonExtensionTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JsonExtensionTest.java index 09ff70673..e2fb9f70a 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JsonExtensionTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JsonExtensionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 DiffPlug + * Copyright 2021-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -63,17 +63,17 @@ void simpleFormattingWithCustomNumberOfSpaces() throws IOException { @Test void gsonDefaultFormatting() throws IOException { setFile("build.gradle").toLines( - "plugins {", - " id 'java'", - " id 'com.diffplug.spotless'", - "}", - "repositories { mavenCentral() }", - "spotless {", - " json {", - " target 'examples/**/*.json'", - " gson()", - "}", - "}"); + "plugins {", + " id 'java'", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " json {", + " target 'examples/**/*.json'", + " gson()", + "}", + "}"); setFile("src/main/resources/example.json").toResource("json/nestedObjectBefore.json"); setFile("examples/main/resources/example.json").toResource("json/nestedObjectBefore.json"); gradleRunner().withArguments("spotlessApply").build(); @@ -84,17 +84,17 @@ void gsonDefaultFormatting() throws IOException { @Test void gsonFormattingWithCustomNumberOfSpaces() throws IOException { setFile("build.gradle").toLines( - "plugins {", - " id 'java'", - " id 'com.diffplug.spotless'", - "}", - "repositories { mavenCentral() }", - "spotless {", - " json {", - " target 'src/**/*.json'", - " gson().indentWithSpaces(6)", - "}", - "}"); + "plugins {", + " id 'java'", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " json {", + " target 'src/**/*.json'", + " gson().indentWithSpaces(6)", + "}", + "}"); setFile("src/main/resources/example.json").toResource("json/singletonArrayBefore.json"); gradleRunner().withArguments("spotlessApply").build(); assertFile("src/main/resources/example.json").sameAsResource("json/singletonArrayAfter6Spaces.json"); @@ -103,17 +103,17 @@ void gsonFormattingWithCustomNumberOfSpaces() throws IOException { @Test void gsonFormattingWithSortingByKeys() throws IOException { setFile("build.gradle").toLines( - "plugins {", - " id 'java'", - " id 'com.diffplug.spotless'", - "}", - "repositories { mavenCentral() }", - "spotless {", - " json {", - " target 'src/**/*.json'", - " gson().sortByKeys()", - "}", - "}"); + "plugins {", + " id 'java'", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " json {", + " target 'src/**/*.json'", + " gson().sortByKeys()", + "}", + "}"); setFile("src/main/resources/example.json").toResource("json/sortByKeysBefore.json"); gradleRunner().withArguments("spotlessApply").build(); assertFile("src/main/resources/example.json").sameAsResource("json/sortByKeysAfter.json"); @@ -122,17 +122,17 @@ void gsonFormattingWithSortingByKeys() throws IOException { @Test void gsonFormattingWithHtmlEscape() throws IOException { setFile("build.gradle").toLines( - "plugins {", - " id 'java'", - " id 'com.diffplug.spotless'", - "}", - "repositories { mavenCentral() }", - "spotless {", - " json {", - " target 'src/**/*.json'", - " gson().escapeHtml()", - "}", - "}"); + "plugins {", + " id 'java'", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " json {", + " target 'src/**/*.json'", + " gson().escapeHtml()", + "}", + "}"); setFile("src/main/resources/example.json").toResource("json/escapeHtmlGsonBefore.json"); gradleRunner().withArguments("spotlessApply").build(); assertFile("src/main/resources/example.json").sameAsResource("json/escapeHtmlGsonAfter.json"); diff --git a/testlib/src/test/java/com/diffplug/spotless/json/GsonStepTest.java b/testlib/src/test/java/com/diffplug/spotless/json/GsonStepTest.java index 80fde970c..a9fdc5383 100644 --- a/testlib/src/test/java/com/diffplug/spotless/json/GsonStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/json/GsonStepTest.java @@ -1,16 +1,30 @@ +/* + * Copyright 2022 DiffPlug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.diffplug.spotless.json; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.Provisioner; - import com.diffplug.spotless.StepHarness; import com.diffplug.spotless.TestProvisioner; import com.diffplug.spotless.json.gson.GsonStep; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; - public class GsonStepTest extends JsonFormatterStepCommonTests { private static final String DEFAULT_VERSION = "2.8.9"; @@ -28,17 +42,17 @@ void handlesObjectWithNull() throws Exception { @Test void handlesInvalidJson() { assertThatThrownBy(() -> doWithResource("invalidJson")) - .isInstanceOf(AssertionError.class) - .hasMessage("Unable to format JSON") - .hasRootCauseMessage("End of input at line 3 column 1 path $.a"); + .isInstanceOf(AssertionError.class) + .hasMessage("Unable to format JSON") + .hasRootCauseMessage("End of input at line 3 column 1 path $.a"); } @Test void handlesNotJson() { assertThatThrownBy(() -> doWithResource("notJson")) - .isInstanceOf(AssertionError.class) - .hasMessage("Unable to format JSON") - .hasNoCause(); + .isInstanceOf(AssertionError.class) + .hasMessage("Unable to format JSON") + .hasNoCause(); } @Test @@ -52,21 +66,21 @@ void handlesSortingWhenSortByKeyEnabled() throws Exception { void doesNoSortingWhenSortByKeyDisabled() throws Exception { FormatterStep step = GsonStep.create(INDENT, false, false, DEFAULT_VERSION, TestProvisioner.mavenCentral()); StepHarness.forStep(step) - .testResource("json/sortByKeysBefore.json", "json/sortByKeysAfterDisabled.json"); + .testResource("json/sortByKeysBefore.json", "json/sortByKeysAfterDisabled.json"); } @Test void handlesHtmlEscapeWhenEnabled() throws Exception { FormatterStep step = GsonStep.create(INDENT, false, true, DEFAULT_VERSION, TestProvisioner.mavenCentral()); StepHarness.forStep(step) - .testResource("json/escapeHtmlGsonBefore.json", "json/escapeHtmlGsonAfter.json"); + .testResource("json/escapeHtmlGsonBefore.json", "json/escapeHtmlGsonAfter.json"); } @Test void writesRawHtmlWhenHtmlEscapeDisabled() throws Exception { FormatterStep step = GsonStep.create(INDENT, false, false, DEFAULT_VERSION, TestProvisioner.mavenCentral()); StepHarness.forStep(step) - .testResource("json/escapeHtmlGsonBefore.json", "json/escapeHtmlGsonAfterDisabled.json"); + .testResource("json/escapeHtmlGsonBefore.json", "json/escapeHtmlGsonAfterDisabled.json"); } @Test @@ -74,8 +88,8 @@ void handlesVersionIncompatibility() { FormatterStep step = GsonStep.create(INDENT, false, false, "1.7", TestProvisioner.mavenCentral()); StepHarness stepHarness = StepHarness.forStep(step); assertThatThrownBy(() -> stepHarness.testResource("json/cucumberJsonSampleGsonBefore.json", "json/cucumberJsonSampleGsonAfter.json")) - .isInstanceOf(IllegalStateException.class) - .hasMessage("There was a problem interacting with Gson; maybe you set an incompatible version?"); + .isInstanceOf(IllegalStateException.class) + .hasMessage("There was a problem interacting with Gson; maybe you set an incompatible version?"); } @Override diff --git a/testlib/src/test/java/com/diffplug/spotless/json/JsonFormatterStepCommonTests.java b/testlib/src/test/java/com/diffplug/spotless/json/JsonFormatterStepCommonTests.java index dd8d9ba69..9ee76f994 100644 --- a/testlib/src/test/java/com/diffplug/spotless/json/JsonFormatterStepCommonTests.java +++ b/testlib/src/test/java/com/diffplug/spotless/json/JsonFormatterStepCommonTests.java @@ -1,10 +1,25 @@ +/* + * Copyright 2022 DiffPlug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.diffplug.spotless.json; -import com.diffplug.spotless.*; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import com.diffplug.spotless.*; public abstract class JsonFormatterStepCommonTests { @@ -13,8 +28,8 @@ public abstract class JsonFormatterStepCommonTests { @Test void cannotProvideNullProvisioner() { assertThatThrownBy(() -> createFormatterStep(INDENT, null)) - .isInstanceOf(NullPointerException.class) - .hasMessage("provisioner cannot be null"); + .isInstanceOf(NullPointerException.class) + .hasMessage("provisioner cannot be null"); } @Test diff --git a/testlib/src/test/java/com/diffplug/spotless/json/JsonSimpleStepTest.java b/testlib/src/test/java/com/diffplug/spotless/json/JsonSimpleStepTest.java index a8b166fa2..b4f98e69b 100644 --- a/testlib/src/test/java/com/diffplug/spotless/json/JsonSimpleStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/json/JsonSimpleStepTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 DiffPlug + * Copyright 2021-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,10 +17,10 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; -import com.diffplug.spotless.*; - import org.junit.jupiter.api.Test; +import com.diffplug.spotless.*; + class JsonSimpleStepTest extends JsonFormatterStepCommonTests { @Test @@ -46,17 +46,17 @@ void handlesObjectWithNull() throws Exception { @Test void handlesInvalidJson() { assertThatThrownBy(() -> doWithResource("invalidJson")) - .isInstanceOf(AssertionError.class) - .hasMessage("Unable to format JSON") - .hasRootCauseMessage("Expected a ',' or '}' at 9 [character 0 line 3]"); + .isInstanceOf(AssertionError.class) + .hasMessage("Unable to format JSON") + .hasRootCauseMessage("Expected a ',' or '}' at 9 [character 0 line 3]"); } @Test void handlesNotJson() { assertThatThrownBy(() -> doWithResource("notJson")) - .isInstanceOf(AssertionError.class) - .hasMessage("Unable to determine JSON type, expected a '{' or '[' but found '#'") - .hasNoCause(); + .isInstanceOf(AssertionError.class) + .hasMessage("Unable to determine JSON type, expected a '{' or '[' but found '#'") + .hasNoCause(); } @Override From baeed60bb788fa30c8ba4a7fd868fa963345fd61 Mon Sep 17 00:00:00 2001 From: Abel Keszei Date: Sat, 12 Feb 2022 19:40:45 +0100 Subject: [PATCH 07/12] add json steps to feature matrix --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6ace09631..733979b1a 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,8 @@ extra('wtp.EclipseWtpFormatterStep') +'{{yes}} | {{yes}} | [`java.PalantirJavaFormatStep`](lib/src/main/java/com/diffplug/spotless/java/PalantirJavaFormatStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | | [`java.RemoveUnusedImportsStep`](lib/src/main/java/com/diffplug/spotless/java/RemoveUnusedImportsStep.java) | :+1: | :+1: | :+1: | :white_large_square: | | [`java.EclipseJdtFormatterStep`](lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java) | :+1: | :+1: | :+1: | :white_large_square: | +| [`json.JsonSimpleStep`](lib/src/main/java/com/diffplug/spotless/json/JsonSimpleStep.java) | :+1: | :white_large_square: | :white_large_square: | :white_large_square: | +| [`json.GsonStep`](lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java) | :+1: | :white_large_square: | :white_large_square: | :white_large_square: | | [`kotlin.KtLintStep`](lib/src/main/java/com/diffplug/spotless/kotlin/KtLintStep.java) | :+1: | :+1: | :+1: | :white_large_square: | | [`kotlin.KtfmtStep`](lib/src/main/java/com/diffplug/spotless/kotlin/KtfmtStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | | [`kotlin.DiktatStep`](lib/src/main/java/com/diffplug/spotless/kotlin/DiktatStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | From 14f4e46992898567b8c94d44406acd016721feb0 Mon Sep 17 00:00:00 2001 From: Abel Keszei Date: Sat, 12 Feb 2022 20:16:11 +0100 Subject: [PATCH 08/12] correctly add json steps to readme --- README.md | 6 ++++-- .../com/diffplug/spotless/json/{gson => }/GsonStep.java | 3 ++- .../java/com/diffplug/gradle/spotless/JsonExtension.java | 2 +- .../test/java/com/diffplug/spotless/json/GsonStepTest.java | 1 - 4 files changed, 7 insertions(+), 5 deletions(-) rename lib/src/main/java/com/diffplug/spotless/json/{gson => }/GsonStep.java (98%) diff --git a/README.md b/README.md index 733979b1a..bf4e7cf0d 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,8 @@ lib('java.ImportOrderStep') +'{{yes}} | {{yes}} lib('java.PalantirJavaFormatStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('java.RemoveUnusedImportsStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |', extra('java.EclipseJdtFormatterStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |', +lib('json.GsonStep') +'{{yes}} | {{no}} | {{no}} | {{no}} |', +lib('json.JsonSimpleStep') +'{{yes}} | {{no}} | {{no}} | {{no}} |', lib('kotlin.KtLintStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |', lib('kotlin.KtfmtStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('kotlin.DiktatStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', @@ -102,8 +104,8 @@ extra('wtp.EclipseWtpFormatterStep') +'{{yes}} | {{yes}} | [`java.PalantirJavaFormatStep`](lib/src/main/java/com/diffplug/spotless/java/PalantirJavaFormatStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | | [`java.RemoveUnusedImportsStep`](lib/src/main/java/com/diffplug/spotless/java/RemoveUnusedImportsStep.java) | :+1: | :+1: | :+1: | :white_large_square: | | [`java.EclipseJdtFormatterStep`](lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java) | :+1: | :+1: | :+1: | :white_large_square: | -| [`json.JsonSimpleStep`](lib/src/main/java/com/diffplug/spotless/json/JsonSimpleStep.java) | :+1: | :white_large_square: | :white_large_square: | :white_large_square: | -| [`json.GsonStep`](lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java) | :+1: | :white_large_square: | :white_large_square: | :white_large_square: | +| [`json.GsonStep`](lib/src/main/java/com/diffplug/spotless/json/GsonStep.java) | :+1: | :white_large_square: | :white_large_square: | :white_large_square: | +| [`json.JsonSimpleStep`](lib/src/main/java/com/diffplug/spotless/json/JsonSimpleStep.java) | :+1: | :white_large_square: | :white_large_square: | :white_large_square: | | [`kotlin.KtLintStep`](lib/src/main/java/com/diffplug/spotless/kotlin/KtLintStep.java) | :+1: | :+1: | :+1: | :white_large_square: | | [`kotlin.KtfmtStep`](lib/src/main/java/com/diffplug/spotless/kotlin/KtfmtStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | | [`kotlin.DiktatStep`](lib/src/main/java/com/diffplug/spotless/kotlin/DiktatStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | diff --git a/lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java b/lib/src/main/java/com/diffplug/spotless/json/GsonStep.java similarity index 98% rename from lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java rename to lib/src/main/java/com/diffplug/spotless/json/GsonStep.java index 06519ae39..81d6ee2f3 100644 --- a/lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java +++ b/lib/src/main/java/com/diffplug/spotless/json/GsonStep.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.diffplug.spotless.json.gson; +package com.diffplug.spotless.json; import java.io.IOException; import java.io.Serializable; @@ -25,6 +25,7 @@ import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.JarState; import com.diffplug.spotless.Provisioner; +import com.diffplug.spotless.json.gson.*; public class GsonStep { private static final String MAVEN_COORDINATES = "com.google.code.gson:gson"; diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JsonExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JsonExtension.java index 25182fa4b..0c4ea3442 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JsonExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JsonExtension.java @@ -18,8 +18,8 @@ import javax.inject.Inject; import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.json.GsonStep; import com.diffplug.spotless.json.JsonSimpleStep; -import com.diffplug.spotless.json.gson.GsonStep; public class JsonExtension extends FormatExtension { private static final int DEFAULT_INDENTATION = 4; diff --git a/testlib/src/test/java/com/diffplug/spotless/json/GsonStepTest.java b/testlib/src/test/java/com/diffplug/spotless/json/GsonStepTest.java index a9fdc5383..822d041aa 100644 --- a/testlib/src/test/java/com/diffplug/spotless/json/GsonStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/json/GsonStepTest.java @@ -23,7 +23,6 @@ import com.diffplug.spotless.Provisioner; import com.diffplug.spotless.StepHarness; import com.diffplug.spotless.TestProvisioner; -import com.diffplug.spotless.json.gson.GsonStep; public class GsonStepTest extends JsonFormatterStepCommonTests { From bd73171360e592628a706a93188d5921f7a4db4d Mon Sep 17 00:00:00 2001 From: Abel Keszei Date: Sun, 13 Feb 2022 13:36:31 +0100 Subject: [PATCH 09/12] updates CHANGES.md --- CHANGES.md | 2 ++ plugin-gradle/CHANGES.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 29fd6d937..e6420824d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,8 @@ This document is intended for Spotless developers. We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`). ## [Unreleased] +### Added +* Added support for JSON formatting based on [Gson](https://github.com/google/gson) ## [2.22.2] - 2022-02-09 ### Changed diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 6c2a6cd4d..52b2b057e 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -3,6 +3,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`). ## [Unreleased] +### Added +* Added support for JSON formatting based on [Gson](https://github.com/google/gson) ## [6.2.2] - 2022-02-09 ### Changed From c8aa57883efd6bfbd177f5ecb7ea39bf5a1f6096 Mon Sep 17 00:00:00 2001 From: Abel Keszei Date: Sun, 13 Feb 2022 13:37:55 +0100 Subject: [PATCH 10/12] add PR links to CHANGES.md changes --- CHANGES.md | 2 +- plugin-gradle/CHANGES.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e6420824d..364fb31be 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,7 +11,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Added -* Added support for JSON formatting based on [Gson](https://github.com/google/gson) +* Added support for JSON formatting based on [Gson](https://github.com/google/gson) ([#1125](https://github.com/diffplug/spotless/pull/1125)). ## [2.22.2] - 2022-02-09 ### Changed diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 52b2b057e..096a40e09 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -4,7 +4,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Added -* Added support for JSON formatting based on [Gson](https://github.com/google/gson) +* Added support for JSON formatting based on [Gson](https://github.com/google/gson) ([#1125](https://github.com/diffplug/spotless/pull/1125)). ## [6.2.2] - 2022-02-09 ### Changed From e6a31deadf0997c80e7a73a1b44ed562f2dcfdb7 Mon Sep 17 00:00:00 2001 From: Abel Keszei Date: Mon, 14 Feb 2022 23:04:36 +0100 Subject: [PATCH 11/12] make classes of com.diffplug.spotless.json.gson package-private --- README.md | 8 ++++---- .../spotless/json/gson/GsonBuilderWrapper.java | 12 ++++++------ .../spotless/json/{ => gson}/GsonStep.java | 3 +-- .../diffplug/spotless/json/gson/GsonWrapper.java | 15 ++++----------- .../spotless/json/gson/GsonWrapperBase.java | 6 +++--- .../spotless/json/gson/JsonElementWrapper.java | 8 ++++---- .../spotless/json/gson/JsonObjectWrapper.java | 12 ++++++------ .../spotless/json/gson/JsonWriterWrapper.java | 10 +++++----- .../diffplug/gradle/spotless/JsonExtension.java | 2 +- .../spotless/json/{ => gson}/GsonStepTest.java | 4 +++- 10 files changed, 37 insertions(+), 43 deletions(-) rename lib/src/main/java/com/diffplug/spotless/json/{ => gson}/GsonStep.java (98%) rename testlib/src/test/java/com/diffplug/spotless/json/{ => gson}/GsonStepTest.java (97%) diff --git a/README.md b/README.md index bf4e7cf0d..f95ea7a7c 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,8 @@ It's easy to build such a function, but there are some gotchas and lots of integ ## Current feature matrix