Skip to content

Commit

Permalink
Convert Gson integration to compile-only source set (#1510)
Browse files Browse the repository at this point in the history
  • Loading branch information
nedtwigg committed Jan 24, 2023
2 parents 2353037 + 92b3cf8 commit c0aad05
Show file tree
Hide file tree
Showing 14 changed files with 197 additions and 384 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
* The default list of type annotations used by `formatAnnotations` has had 8 more annotations from the Checker Framework added [#1494](https://github.com/diffplug/spotless/pull/1494)
### Changes
* Rename `YamlJacksonStep` into `JacksonYamlStep` while normalizing Jackson usage ([#1492](https://github.com/diffplug/spotless/pull/1492))
* Convert `gson` integration to use a compile-only source set ([#1510](https://github.com/diffplug/spotless/pull/1510)).

## [2.32.0] - 2023-01-13
### Added
Expand Down
5 changes: 4 additions & 1 deletion lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ def NEEDS_GLUE = [
'flexmark',
'diktat',
'scalafmt',
'jackson'
'jackson',
'gson'
]
for (glue in NEEDS_GLUE) {
sourceSets.register(glue) {
Expand Down Expand Up @@ -108,6 +109,8 @@ dependencies {

// used for markdown formatting
flexmarkCompileOnly 'com.vladsch.flexmark:flexmark-all:0.62.2'

gsonCompileOnly 'com.google.code.gson:gson:2.10.1'
}

// we'll hold the core lib to a high standard
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright 2023 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.glue.gson;

import java.io.IOException;
import java.io.StringWriter;
import java.util.Collections;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.stream.JsonWriter;

import com.diffplug.spotless.FormatterFunc;
import com.diffplug.spotless.ThrowingEx;
import com.diffplug.spotless.json.gson.GsonConfig;

public class GsonFormatterFunc implements FormatterFunc {

private static final String FAILED_TO_PARSE_ERROR_MESSAGE = "Unable to format JSON";

private final Gson gson;
private final GsonConfig gsonConfig;
private final String generatedIndent;

public GsonFormatterFunc(GsonConfig gsonConfig) {
GsonBuilder gsonBuilder = new GsonBuilder().serializeNulls();
if (!gsonConfig.isEscapeHtml()) {
gsonBuilder = gsonBuilder.disableHtmlEscaping();
}
this.gson = gsonBuilder.create();
this.gsonConfig = gsonConfig;
this.generatedIndent = generateIndent(gsonConfig.getIndentSpaces());
}

@Override
public String apply(String inputString) {
String result;
if (inputString.isEmpty()) {
result = "";
} else {
JsonElement jsonElement = gson.fromJson(inputString, JsonElement.class);
if (jsonElement == null) {
throw new AssertionError(FAILED_TO_PARSE_ERROR_MESSAGE);
}
if (gsonConfig.isSortByKeys() && jsonElement.isJsonObject()) {
jsonElement = sortByKeys(jsonElement.getAsJsonObject());
}
try (StringWriter stringWriter = new StringWriter()) {
JsonWriter jsonWriter = new JsonWriter(stringWriter);
jsonWriter.setIndent(this.generatedIndent);
gson.toJson(jsonElement, jsonWriter);
result = stringWriter + "\n";
} catch (IOException ioException) {
throw ThrowingEx.asRuntime(ioException);
}
}
return result;
}

private JsonElement sortByKeys(JsonObject jsonObject) {
JsonObject result = new JsonObject();
jsonObject.keySet().stream().sorted()
.forEach(key -> {
JsonElement element = jsonObject.get(key);
if (element.isJsonObject()) {
element = sortByKeys(element.getAsJsonObject());
}
result.add(key, element);
});
return result;
}

private String generateIndent(int indentSpaces) {
return String.join("", Collections.nCopies(indentSpaces, " "));
}

}

This file was deleted.

66 changes: 66 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/json/gson/GsonConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2023 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.io.Serializable;

public class GsonConfig implements Serializable {
private static final long serialVersionUID = 6039715618937332633L;

private boolean sortByKeys;
private boolean escapeHtml;
private int indentSpaces;
private String version;

public GsonConfig(boolean sortByKeys, boolean escapeHtml, int indentSpaces, String version) {
this.sortByKeys = sortByKeys;
this.escapeHtml = escapeHtml;
this.indentSpaces = indentSpaces;
this.version = version;
}

public boolean isSortByKeys() {
return sortByKeys;
}

public void setSortByKeys(boolean sortByKeys) {
this.sortByKeys = sortByKeys;
}

public boolean isEscapeHtml() {
return escapeHtml;
}

public void setEscapeHtml(boolean escapeHtml) {
this.escapeHtml = escapeHtml;
}

public int getIndentSpaces() {
return indentSpaces;
}

public void setIndentSpaces(int indentSpaces) {
this.indentSpaces = indentSpaces;
}

public String getVersion() {
return version;
}

public void setVersion(String version) {
this.version = version;
}
}
84 changes: 22 additions & 62 deletions lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 DiffPlug
* Copyright 2022-2023 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,8 +17,8 @@

import java.io.IOException;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.Collections;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;

import com.diffplug.spotless.FormatterFunc;
Expand All @@ -28,78 +28,38 @@

public class GsonStep {
private static final String MAVEN_COORDINATES = "com.google.code.gson:gson";
private static final String INCOMPATIBLE_ERROR_MESSAGE = "There was a problem interacting with Gson; maybe you set an incompatible version?";

@Deprecated
public static FormatterStep create(int indentSpaces, boolean sortByKeys, boolean escapeHtml, String version, Provisioner provisioner) {
return create(new GsonConfig(sortByKeys, escapeHtml, indentSpaces, version), provisioner);
}

public static FormatterStep create(GsonConfig gsonConfig, Provisioner provisioner) {
Objects.requireNonNull(provisioner, "provisioner cannot be null");
return FormatterStep.createLazy("gson", () -> new State(indentSpaces, sortByKeys, escapeHtml, version, provisioner), State::toFormatter);
return FormatterStep.createLazy("gson", () -> new State(gsonConfig, provisioner), State::toFormatter);
}

private static final class State implements Serializable {
private static final long serialVersionUID = -1493479043249379485L;
private static final long serialVersionUID = -3240568265160440420L;

private final int indentSpaces;
private final boolean sortByKeys;
private final boolean escapeHtml;
private final JarState jarState;
private final GsonConfig gsonConfig;

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);
private State(GsonConfig gsonConfig, Provisioner provisioner) throws IOException {
this.gsonConfig = gsonConfig;
this.jarState = JarState.from(MAVEN_COORDINATES + ":" + gsonConfig.getVersion(), provisioner);
}

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 gsonBuilder = gsonBuilderWrapper.serializeNulls(gsonBuilderWrapper.createGsonBuilder());
if (!escapeHtml) {
gsonBuilder = gsonBuilderWrapper.disableHtmlEscaping(gsonBuilder);
try {
Class<?> formatterFunc = jarState.getClassLoader().loadClass("com.diffplug.spotless.glue.gson.GsonFormatterFunc");
Constructor<?> constructor = formatterFunc.getConstructor(GsonConfig.class);
return (FormatterFunc) constructor.newInstance(gsonConfig);
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException
| InstantiationException | IllegalAccessException | NoClassDefFoundError cause) {
throw new IllegalStateException(INCOMPATIBLE_ERROR_MESSAGE, cause);
}
Object gson = gsonBuilderWrapper.create(gsonBuilder);

return inputString -> {
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;
};
}

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, " "));
}
}

Expand Down
41 changes: 0 additions & 41 deletions lib/src/main/java/com/diffplug/spotless/json/gson/GsonWrapper.java

This file was deleted.

0 comments on commit c0aad05

Please sign in to comment.