Skip to content

Commit 07bafaf

Browse files
authored
YAML implementation with Jackson (#1478), also some fixes to the maven json plugin.
2 parents 219d6a8 + fa203e4 commit 07bafaf

21 files changed

+503
-11
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,6 @@ nbdist/
121121
nbactions.xml
122122
nb-configuration.xml
123123
.nb-gradle/
124+
125+
# MacOS jenv
126+
.java-version

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
1414
* Add option `editorConfigFile` for `ktLint` [#142](https://github.com/diffplug/spotless/issues/142)
1515
* **POTENTIALLY BREAKING** `ktlint` step now modifies license headers. Make sure to put `licenseHeader` *after* `ktlint`.
1616
* Added `skipLinesMatching` option to `licenseHeader` to support formats where license header cannot be immediately added to the top of the file (e.g. xml, sh). ([#1441](https://github.com/diffplug/spotless/pull/1441)).
17+
* Add YAML support through Jackson ([#1478](https://github.com/diffplug/spotless/pull/1478))
1718
### Fixed
1819
* Support `ktlint` 0.48+ new rule disabling syntax ([#1456](https://github.com/diffplug/spotless/pull/1456)) fixes ([#1444](https://github.com/diffplug/spotless/issues/1444))
1920
* Added support for npm-based [ESLint](https://eslint.org/)-formatter for javascript and typescript ([#1453](https://github.com/diffplug/spotless/pull/1453))

lib/build.gradle

+5-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ def NEEDS_GLUE = [
1414
'ktlint',
1515
'flexmark',
1616
'diktat',
17-
'scalafmt'
17+
'scalafmt',
18+
'jackson'
1819
]
1920
for (glue in NEEDS_GLUE) {
2021
sourceSets.register(glue) {
@@ -55,6 +56,9 @@ dependencies {
5556

5657
palantirJavaFormatCompileOnly 'com.palantir.javaformat:palantir-java-format:1.1.0' // this version needs to stay compilable against Java 8 for CI Job testNpm
5758

59+
// used jackson-based formatters
60+
jacksonCompileOnly 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.14.1'
61+
5862
String VER_KTFMT = '0.42'
5963
ktfmtCompileOnly "com.facebook:ktfmt:$VER_KTFMT"
6064
String VER_KTLINT_GOOGLE_JAVA_FORMAT = '1.7' // for JDK 8 compatibility
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright 2021-2023 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.glue.yaml;
17+
18+
import java.io.IOException;
19+
import java.util.List;
20+
21+
import com.fasterxml.jackson.core.JsonProcessingException;
22+
import com.fasterxml.jackson.core.type.TypeReference;
23+
import com.fasterxml.jackson.databind.ObjectMapper;
24+
import com.fasterxml.jackson.databind.SerializationFeature;
25+
import com.fasterxml.jackson.databind.node.ObjectNode;
26+
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
27+
28+
import com.diffplug.spotless.FormatterFunc;
29+
30+
public class YamlJacksonFormatterFunc implements FormatterFunc {
31+
private List<String> enabledFeatures;
32+
private List<String> disabledFeatures;
33+
34+
public YamlJacksonFormatterFunc(List<String> enabledFeatures, List<String> disabledFeatures) {
35+
this.enabledFeatures = enabledFeatures;
36+
this.disabledFeatures = disabledFeatures;
37+
}
38+
39+
@Override
40+
public String apply(String input) throws Exception {
41+
ObjectMapper objectMapper = makeObjectMapper();
42+
43+
return format(objectMapper, input);
44+
}
45+
46+
protected ObjectMapper makeObjectMapper() {
47+
YAMLFactory yamlFactory = new YAMLFactory();
48+
ObjectMapper objectMapper = new ObjectMapper(yamlFactory);
49+
50+
// Configure the ObjectMapper
51+
// https://github.com/FasterXML/jackson-databind#commonly-used-features
52+
for (String rawFeature : enabledFeatures) {
53+
// https://stackoverflow.com/questions/3735927/java-instantiating-an-enum-using-reflection
54+
SerializationFeature feature = SerializationFeature.valueOf(rawFeature);
55+
56+
objectMapper.enable(feature);
57+
}
58+
59+
for (String rawFeature : disabledFeatures) {
60+
// https://stackoverflow.com/questions/3735927/java-instantiating-an-enum-using-reflection
61+
SerializationFeature feature = SerializationFeature.valueOf(rawFeature);
62+
63+
objectMapper.disable(feature);
64+
}
65+
return objectMapper;
66+
}
67+
68+
protected String format(ObjectMapper objectMapper, String input) throws IllegalArgumentException, IOException {
69+
// We may consider adding manually an initial '---' prefix to help management of multiple documents
70+
// if (!input.trim().startsWith("---")) {
71+
// input = "---" + "\n" + input;
72+
// }
73+
74+
try {
75+
// https://stackoverflow.com/questions/25222327/deserialize-pojos-from-multiple-yaml-documents-in-a-single-file-in-jackson
76+
// https://github.com/FasterXML/jackson-dataformats-text/issues/66#issuecomment-375328648
77+
// 2023-01: For now, we get 'Cannot deserialize value of type `com.fasterxml.jackson.databind.node.ObjectNode` from Array value'
78+
// JsonParser yamlParser = objectMapper.getFactory().createParser(input);
79+
// List<ObjectNode> docs = objectMapper.readValues(yamlParser, ObjectNode.class).readAll();
80+
// return objectMapper.writeValueAsString(docs);
81+
82+
// 2023-01: This returns JSON instead of YAML
83+
// This will transit with a JsonNode
84+
// A JsonNode may keep the comments from the input node
85+
// JsonNode jsonNode = objectMapper.readTree(input);
86+
//Not 'toPrettyString' as one could require no INDENT_OUTPUT
87+
// return jsonNode.toPrettyString();
88+
ObjectNode objectNode = objectMapper.readValue(input, ObjectNode.class);
89+
return objectMapper.writeValueAsString(objectNode);
90+
} catch (JsonProcessingException e) {
91+
throw new AssertionError("Unable to format YAML. input='" + input + "'", e);
92+
}
93+
}
94+
95+
// Spotbugs
96+
private static class ObjectNodeTypeReference extends TypeReference<ObjectNode> {}
97+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2021-2023 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.yaml;
17+
18+
import java.io.IOException;
19+
import java.io.Serializable;
20+
import java.lang.reflect.Constructor;
21+
import java.lang.reflect.InvocationTargetException;
22+
import java.util.Arrays;
23+
import java.util.List;
24+
import java.util.Objects;
25+
26+
import com.diffplug.spotless.FormatterFunc;
27+
import com.diffplug.spotless.FormatterStep;
28+
import com.diffplug.spotless.JarState;
29+
import com.diffplug.spotless.Provisioner;
30+
31+
/**
32+
* Simple YAML formatter which reformats the file according to Jackson YAMLFactory.
33+
*/
34+
// https://stackoverflow.com/questions/14515994/convert-json-string-to-pretty-print-json-output-using-jackson
35+
public class YamlJacksonStep {
36+
static final String MAVEN_COORDINATE = "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:";
37+
// https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml
38+
static final String DEFAULT_VERSION = "2.14.1";
39+
40+
private YamlJacksonStep() {}
41+
42+
public static String defaultVersion() {
43+
return DEFAULT_VERSION;
44+
}
45+
46+
public static FormatterStep create(List<String> enabledFeatures,
47+
List<String> disabledFeatures,
48+
String jacksonVersion,
49+
Provisioner provisioner) {
50+
Objects.requireNonNull(provisioner, "provisioner cannot be null");
51+
return FormatterStep.createLazy("yaml",
52+
() -> new State(enabledFeatures, disabledFeatures, jacksonVersion, provisioner),
53+
State::toFormatter);
54+
}
55+
56+
public static FormatterStep create(Provisioner provisioner) {
57+
return create(Arrays.asList("INDENT_OUTPUT"), Arrays.asList(), defaultVersion(), provisioner);
58+
}
59+
60+
private static final class State implements Serializable {
61+
private static final long serialVersionUID = 1L;
62+
63+
private final List<String> enabledFeatures;
64+
private final List<String> disabledFeatures;
65+
66+
private final JarState jarState;
67+
68+
private State(List<String> enabledFeatures,
69+
List<String> disabledFeatures,
70+
String jacksonVersion,
71+
Provisioner provisioner) throws IOException {
72+
this.enabledFeatures = enabledFeatures;
73+
this.disabledFeatures = disabledFeatures;
74+
75+
this.jarState = JarState.from(YamlJacksonStep.MAVEN_COORDINATE + jacksonVersion, provisioner);
76+
}
77+
78+
FormatterFunc toFormatter() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
79+
InstantiationException, IllegalAccessException {
80+
Class<?> formatterFunc = jarState.getClassLoader().loadClass("com.diffplug.spotless.glue.yaml.YamlJacksonFormatterFunc");
81+
Constructor<?> constructor = formatterFunc.getConstructor(List.class, List.class);
82+
return (FormatterFunc) constructor.newInstance(enabledFeatures, disabledFeatures);
83+
}
84+
}
85+
}

plugin-maven/CHANGES.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
55
## [Unreleased]
66
### Added
77
* Add option `editorConfigFile` for `ktLint` [#142](https://github.com/diffplug/spotless/issues/142)
8-
* **POTENTIALLY BREAKING** `ktlint` step now modifies license headers. Make sure to put `licenseHeader` *after* `ktlint`.
8+
* **POTENTIALLY BREAKING** `ktlint` step now modifies license headers. Make sure to put `licenseHeader` *after* `ktlint`.
99
* Added `skipLinesMatching` option to `licenseHeader` to support formats where license header cannot be immediately added to the top of the file (e.g. xml, sh). ([#1441](https://github.com/diffplug/spotless/pull/1441))
1010
* Add JSON support ([#1446](https://github.com/diffplug/spotless/pull/1446))
11+
* Add YAML support through Jackson ([#1478](https://github.com/diffplug/spotless/pull/1478))
1112
### Fixed
1213
* Support `ktlint` 0.48+ new rule disabling syntax ([#1456](https://github.com/diffplug/spotless/pull/1456)) fixes ([#1444](https://github.com/diffplug/spotless/issues/1444))
1314
* Added support for npm-based [ESLint](https://eslint.org/)-formatter for javascript and typescript ([#1453](https://github.com/diffplug/spotless/pull/1453))

plugin-maven/README.md

+37-1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ user@machine repo % mvn spotless:check
6060
- [Typescript](#typescript) ([tsfmt](#tsfmt), [prettier](#prettier), [ESLint](#eslint-typescript))
6161
- [Javascript](#javascript) ([prettier](#prettier), [ESLint](#eslint-javascript))
6262
- [JSON](#json)
63+
- [YAML](#yaml)
6364
- Multiple languages
6465
- [Prettier](#prettier) ([plugins](#prettier-plugins), [npm detection](#npm-detection), [`.npmrc` detection](#npmrc-detection))
6566
- [eclipse web tools platform](#eclipse-web-tools-platform)
@@ -852,7 +853,7 @@ For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#n
852853

853854
## JSON
854855

855-
- `com.diffplug.spotless.maven.json.Json` [code](https://github.com/diffplug/spotless/blob/main/plugin-maven/src/main/java/com/diffplug/spotless/maven/json/json.java)
856+
- `com.diffplug.spotless.maven.json.Json` [code](https://github.com/diffplug/spotless/blob/main/plugin-maven/src/main/java/com/diffplug/spotless/maven/json/Json.java)
856857

857858
```xml
858859
<configuration>
@@ -899,6 +900,41 @@ for details.
899900

900901
<a name="applying-prettier-to-javascript--flow--typescript--css--scss--less--jsx--graphql--yaml--etc"></a>
901902

903+
904+
## YAML
905+
906+
- `com.diffplug.spotless.maven.FormatterFactory.addStepFactory(FormatterStepFactory)` [code](https://github.com/diffplug/spotless/blob/main/plugin-maven/src/main/java/com/diffplug/spotless/maven/yaml/Yaml.java)
907+
908+
```xml
909+
<configuration>
910+
<yaml>
911+
<includes> <!-- You have to set the target manually -->
912+
<include>src/**/*.yaml</include>
913+
</includes>
914+
915+
<jackson /> <!-- has its own section below -->
916+
</yaml>
917+
</configuration>
918+
```
919+
920+
### jackson
921+
922+
Uses Jackson and YAMLFactory to pretty print objects:
923+
924+
```xml
925+
<jackson>
926+
<version>2.14.1</version> <!-- optional: The version of 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml' to be used -->
927+
<enabledFeatures> <!-- optional: Customize the set of enabled features -->
928+
<enabledFeature>INDENT_OUTPUT<enabledFeature/>
929+
</enabledFeatures>
930+
<disabledFeatures> <!-- optional: Customize the set of disabled features -->
931+
<disabledFeature>DEFAULT_HAS_NO_DISABLED_FEATURE<disabledFeature/>
932+
</disabledFeatures>
933+
</jackson>
934+
```
935+
936+
<a name="applying-prettier-to-javascript--flow--typescript--css--scss--less--jsx--graphql--yaml--etc"></a>
937+
902938
## Prettier
903939

904940
[homepage](https://prettier.io/). [changelog](https://github.com/prettier/prettier/blob/master/CHANGELOG.md). [official plugins](https://prettier.io/docs/en/plugins.html#official-plugins). [community plugins](https://prettier.io/docs/en/plugins.html#community-plugins). Prettier is a formatter that can format almost every anything - JavaScript, JSX, Angular, Vue, Flow, TypeScript, CSS, Less, SCSS, HTML, JSON, GraphQL, Markdown (including GFM and MDX), and YAML. It can format even more [using plugins](https://prettier.io/docs/en/plugins.html) (PHP, Ruby, Swift, XML, Apex, Elm, Java (!!), Kotlin, pgSQL, .properties, solidity, svelte, toml, shellscript, ...).

plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,15 @@
6565
import com.diffplug.spotless.maven.incremental.UpToDateChecking;
6666
import com.diffplug.spotless.maven.java.Java;
6767
import com.diffplug.spotless.maven.javascript.Javascript;
68+
import com.diffplug.spotless.maven.json.Json;
6869
import com.diffplug.spotless.maven.kotlin.Kotlin;
6970
import com.diffplug.spotless.maven.markdown.Markdown;
7071
import com.diffplug.spotless.maven.pom.Pom;
7172
import com.diffplug.spotless.maven.python.Python;
7273
import com.diffplug.spotless.maven.scala.Scala;
7374
import com.diffplug.spotless.maven.sql.Sql;
7475
import com.diffplug.spotless.maven.typescript.Typescript;
76+
import com.diffplug.spotless.maven.yaml.Yaml;
7577

7678
public abstract class AbstractSpotlessMojo extends AbstractMojo {
7779
private static final String DEFAULT_INDEX_FILE_NAME = "spotless-index";
@@ -171,6 +173,12 @@ public abstract class AbstractSpotlessMojo extends AbstractMojo {
171173
@Parameter
172174
private Markdown markdown;
173175

176+
@Parameter
177+
private Json json;
178+
179+
@Parameter
180+
private Yaml yaml;
181+
174182
@Parameter(property = "spotlessFiles")
175183
private String filePatterns;
176184

@@ -335,7 +343,7 @@ private FileLocator getFileLocator() {
335343
}
336344

337345
private List<FormatterFactory> getFormatterFactories() {
338-
return Stream.concat(formats.stream(), Stream.of(groovy, java, scala, kotlin, cpp, typescript, javascript, antlr4, pom, sql, python, markdown))
346+
return Stream.concat(formats.stream(), Stream.of(groovy, java, scala, kotlin, cpp, typescript, javascript, antlr4, pom, sql, python, markdown, json, yaml))
339347
.filter(Objects::nonNull)
340348
.collect(toList());
341349
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2023 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.maven.yaml;
17+
18+
import java.util.Arrays;
19+
import java.util.List;
20+
21+
import org.apache.maven.plugins.annotations.Parameter;
22+
23+
import com.diffplug.spotless.FormatterStep;
24+
import com.diffplug.spotless.maven.FormatterStepConfig;
25+
import com.diffplug.spotless.maven.FormatterStepFactory;
26+
import com.diffplug.spotless.yaml.YamlJacksonStep;
27+
28+
public class Jackson implements FormatterStepFactory {
29+
30+
@Parameter
31+
private String version = YamlJacksonStep.defaultVersion();
32+
33+
@Parameter
34+
private String[] enabledFeatures = new String[]{"INDENT_OUTPUT"};
35+
36+
@Parameter
37+
private String[] disabledFeatures = new String[0];
38+
39+
@Override
40+
public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) {
41+
List<String> enabledFeaturesAsList = Arrays.asList(enabledFeatures);
42+
List<String> disabledFeaturesAsList = Arrays.asList(disabledFeatures);
43+
return YamlJacksonStep
44+
.create(enabledFeaturesAsList, disabledFeaturesAsList, version, stepConfig.getProvisioner());
45+
}
46+
}

0 commit comments

Comments
 (0)