Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert Gson integration to compile-only source set #1510

Merged
merged 6 commits into from
Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,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) {
nedtwigg marked this conversation as resolved.
Show resolved Hide resolved
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.