Skip to content

fix #1076: Add support for palantir-java-format #1083

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

Merged
merged 15 commits into from
Jan 12, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ jobs:
docker:
- image: cimg/openjdk:11.0
<<: *test_nomaven
test_nomaven_15:
# latest JDK, replace with 16 when it comes out
test_nomaven_17:
# latest JDK
<<: *env_gradle_large
docker:
- image: cimg/openjdk:15.0
- image: cimg/openjdk:17.0
<<: *test_nomaven
test_justmaven_11:
<< : *env_gradle
Expand Down Expand Up @@ -275,7 +275,7 @@ workflows:
- test_nomaven_11:
requires:
- assemble_testClasses
- test_nomaven_15:
- test_nomaven_17:
requires:
- assemble_testClasses
- test_npm_8:
Expand Down
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 the [palantir-java-format](https://github.com/palantir/palantir-java-format) Java formatter ([#1083](https://github.com/diffplug/spotless/pull/1083)).

## [2.21.2] - 2022-01-07
### Fixed
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ extra('cpp.EclipseFormatterStep') +'{{yes}} | {{yes}}
extra('groovy.GrEclipseFormatterStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
lib('java.GoogleJavaFormatStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
lib('java.ImportOrderStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
lib('java.PalantirJavaFormatStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
lib('java.RemoveUnusedImportsStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
extra('java.EclipseJdtFormatterStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
lib('kotlin.KtLintStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
Expand Down Expand Up @@ -98,6 +99,7 @@ extra('wtp.EclipseWtpFormatterStep') +'{{yes}} | {{yes}}
| [`groovy.GrEclipseFormatterStep`](lib-extra/src/main/java/com/diffplug/spotless/extra/groovy/GrEclipseFormatterStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
| [`java.GoogleJavaFormatStep`](lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
| [`java.ImportOrderStep`](lib/src/main/java/com/diffplug/spotless/java/ImportOrderStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
| [`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: |
| [`kotlin.KtLintStep`](lib/src/main/java/com/diffplug/spotless/kotlin/KtLintStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
Expand Down
3 changes: 3 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# To fix metaspace errors
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

name=spotless
description=Spotless - keep your code spotless with Gradle
org=diffplug
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
* 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.
* 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.java;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Objects;

import com.diffplug.spotless.*;
import com.diffplug.spotless.ThrowingEx.Function;

/** Wraps up <a href="https://github.com/palantir/palantir-java-format">palantir-java-format</a> fork of
* <a href="https://github.com/google/google-java-format">google-java-format</a> as a FormatterStep. */
public class PalantirJavaFormatStep {
// prevent direct instantiation
private PalantirJavaFormatStep() {}

private static final String DEFAULT_STYLE = "PALANTIR";
static final String NAME = "palantir-java-format";
static final String MAVEN_COORDINATE = "com.palantir.javaformat:palantir-java-format";
static final String FORMATTER_CLASS = "com.palantir.javaformat.java.Formatter";
static final String FORMATTER_METHOD = "formatSource";

private static final String OPTIONS_CLASS = "com.palantir.javaformat.java.JavaFormatterOptions";
private static final String OPTIONS_BUILDER_METHOD = "builder";
private static final String OPTIONS_BUILDER_CLASS = "com.palantir.javaformat.java.JavaFormatterOptions$Builder";
private static final String OPTIONS_BUILDER_STYLE_METHOD = "style";
private static final String OPTIONS_BUILDER_BUILD_METHOD = "build";
private static final String OPTIONS_STYLE = "com.palantir.javaformat.java.JavaFormatterOptions$Style";

private static final String REMOVE_UNUSED_CLASS = "com.palantir.javaformat.java.RemoveUnusedImports";
private static final String REMOVE_UNUSED_METHOD = "removeUnusedImports";

private static final String IMPORT_ORDERER_CLASS = "com.palantir.javaformat.java.ImportOrderer";
private static final String IMPORT_ORDERER_METHOD = "reorderImports";

/** Creates a step which formats everything - code, import order, and unused imports. */
public static FormatterStep create(Provisioner provisioner) {
return create(defaultVersion(), provisioner);
}

/** Creates a step which formats everything - code, import order, and unused imports. */
public static FormatterStep create(String version, Provisioner provisioner) {
return create(version, DEFAULT_STYLE, provisioner);
}

/** Creates a step which formats everything - code, import order, and unused imports. */
public static FormatterStep create(String version, String style, Provisioner provisioner) {
return create(MAVEN_COORDINATE, version, style, provisioner);
}

/** Creates a step which formats everything - groupArtifact, code, import order, and unused imports. */
public static FormatterStep create(String groupArtifact, String version, String style, Provisioner provisioner) {
Objects.requireNonNull(groupArtifact, "groupArtifact");
if (groupArtifact.chars().filter(ch -> ch == ':').count() != 1) {
throw new IllegalArgumentException("groupArtifact must be in the form 'groupId:artifactId'");
}
Objects.requireNonNull(version, "version");
Objects.requireNonNull(style, "style");
Objects.requireNonNull(provisioner, "provisioner");
return FormatterStep.createLazy(NAME,
() -> new State(NAME, groupArtifact, version, style, provisioner),
State::createFormat);
}

static final Jvm.Support<String> JVM_SUPPORT = Jvm.<String> support(NAME).add(8, "1.1.0").add(11, "2.10.0");

public static String defaultGroupArtifact() {
return MAVEN_COORDINATE;
}

/** Get default formatter version */
public static String defaultVersion() {
return JVM_SUPPORT.getRecommendedFormatterVersion();
}

public static String defaultStyle() {
return DEFAULT_STYLE;
}

static final class State implements Serializable {
private static final long serialVersionUID = 1L;

/** The jar that contains the formatter. */
final JarState jarState;
final String stepName;
final String version;
final String style;

State(String stepName, String groupArtifact, String version, String style, Provisioner provisioner) throws Exception {
JVM_SUPPORT.assertFormatterSupported(version);
this.jarState = JarState.from(groupArtifact + ":" + version, provisioner);
this.stepName = stepName;
this.version = version;
this.style = style;
}

@SuppressWarnings({"unchecked", "rawtypes"})
FormatterFunc createFormat() throws Exception {
ClassLoader classLoader = jarState.getClassLoader();

// instantiate the formatter and get its format method
Class<?> optionsClass = classLoader.loadClass(OPTIONS_CLASS);
Class<?> optionsBuilderClass = classLoader.loadClass(OPTIONS_BUILDER_CLASS);
Method optionsBuilderMethod = optionsClass.getMethod(OPTIONS_BUILDER_METHOD);
Object optionsBuilder = optionsBuilderMethod.invoke(null);

Class<?> optionsStyleClass = classLoader.loadClass(OPTIONS_STYLE);
Object styleConstant = Enum.valueOf((Class<Enum>) optionsStyleClass, style);
Method optionsBuilderStyleMethod = optionsBuilderClass.getMethod(OPTIONS_BUILDER_STYLE_METHOD, optionsStyleClass);
optionsBuilderStyleMethod.invoke(optionsBuilder, styleConstant);

Method optionsBuilderBuildMethod = optionsBuilderClass.getMethod(OPTIONS_BUILDER_BUILD_METHOD);
Object options = optionsBuilderBuildMethod.invoke(optionsBuilder);

Class<?> formatterClazz = classLoader.loadClass(FORMATTER_CLASS);
Object formatter = formatterClazz.getMethod("createFormatter", optionsClass).invoke(null, options);
Method formatterMethod = formatterClazz.getMethod(FORMATTER_METHOD, String.class);

Function<String, String> removeUnused = constructRemoveUnusedFunction(classLoader);

Class<?> importOrdererClass = classLoader.loadClass(IMPORT_ORDERER_CLASS);
Method importOrdererMethod = importOrdererClass.getMethod(IMPORT_ORDERER_METHOD, String.class);

return JVM_SUPPORT.suggestLaterVersionOnError(version, (input -> {
String formatted = (String) formatterMethod.invoke(formatter, input);
String removedUnused = removeUnused.apply(formatted);
return (String) importOrdererMethod.invoke(null, removedUnused);
}));
}

private static Function<String, String> constructRemoveUnusedFunction(ClassLoader classLoader)
throws NoSuchMethodException, ClassNotFoundException {
Class<?> removeUnusedClass = classLoader.loadClass(REMOVE_UNUSED_CLASS);
Method removeUnusedMethod = removeUnusedClass.getMethod(REMOVE_UNUSED_METHOD, String.class);
return (x) -> (String) removeUnusedMethod.invoke(null, x);
}
}
}
2 changes: 2 additions & 0 deletions plugin-gradle/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 the [palantir-java-format](https://github.com/palantir/palantir-java-format) Java formatter ([#1083](https://github.com/diffplug/spotless/pull/1083)).

## [6.1.2] - 2022-01-07
### Fixed
Expand Down
29 changes: 28 additions & 1 deletion plugin-gradle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Spotless supports all of Gradle's built-in performance features (incremental bui
- [**Quickstart**](#quickstart)
- [Requirements](#requirements)
- **Languages**
- [Java](#java) ([google-java-format](#google-java-format), [eclipse jdt](#eclipse-jdt), [clang-format](#clang-format), [prettier](#prettier))
- [Java](#java) ([google-java-format](#google-java-format), [eclipse jdt](#eclipse-jdt), [clang-format](#clang-format), [prettier](#prettier), [palantir-java-format](#palantir-java-format))
- [Groovy](#groovy) ([eclipse groovy](#eclipse-groovy))
- [Kotlin](#kotlin) ([ktfmt](#ktfmt), [ktlint](#ktlint), [diktat](#diktat), [prettier](#prettier))
- [Scala](#scala) ([scalafmt](#scalafmt))
Expand Down Expand Up @@ -203,6 +203,33 @@ org.gradle.jvmargs=--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAME
```
This is a workaround to a [pending issue](https://github.com/diffplug/spotless/issues/834).

### palantir-java-format

[homepage](https://github.com/palantir/palantir-java-format). [changelog](https://github.com/palantir/palantir-java-format/releases).
```gradle
spotless {
java {
palantirJavaFormat()
// optional: you can specify a specific version and/or switch to GOOGLE/AOSP style
// and/or use custom group artifact (you probably don't need this)
palantirJavaFormat('2.9.0').style("PALANTIR").groupArtifact('com.google.googlejavaformat:google-java-format')
```

**⚠️ Note on using Palantir Java Format with Java 16+**

Using Java 16+ with Palantir Java Format [requires additional flags](https://github.com/google/google-java-format/releases/tag/v1.10.0) on the running JDK.
These Flags can be provided using the `gradle.properties` file (See [documentation](https://docs.gradle.org/current/userguide/build_environment.html)).

For example the following file under `gradle.properties` will run maven with the required flags:
```
org.gradle.jvmargs=--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \
--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
```
This is a workaround to a [pending issue](https://github.com/diffplug/spotless/issues/834).

### eclipse jdt

[homepage](https://www.eclipse.org/downloads/packages/). [compatible versions](https://github.com/diffplug/spotless/tree/main/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter). See [here](../ECLIPSE_SCREENSHOTS.md) for screenshots that demonstrate how to get and install the config file mentioned below.
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -34,6 +34,7 @@
import com.diffplug.spotless.generic.LicenseHeaderStep;
import com.diffplug.spotless.java.GoogleJavaFormatStep;
import com.diffplug.spotless.java.ImportOrderStep;
import com.diffplug.spotless.java.PalantirJavaFormatStep;
import com.diffplug.spotless.java.RemoveUnusedImportsStep;

public class JavaExtension extends FormatExtension implements HasBuiltinDelimiterForLicense {
Expand Down Expand Up @@ -175,6 +176,55 @@ private FormatterStep createStep() {
}
}

/** Uses the <a href="https://github.com/palantir/palantir-java-format">palantir-java-format</a> jar to format source code. */
public PalantirJavaFormatConfig palantirJavaFormat() {
return palantirJavaFormat(PalantirJavaFormatStep.defaultVersion());
}

/**
* Uses the given version of <a href="https://github.com/palantir/palantir-java-format">palantir-java-format</a> to format source code.
*
* Limited to published versions. See <a href="https://github.com/diffplug/spotless/issues/33#issuecomment-252315095">issue #33</a>
* for an workaround for using snapshot versions.
*/
public PalantirJavaFormatConfig palantirJavaFormat(String version) {
Objects.requireNonNull(version);
return new PalantirJavaFormatConfig(version);
}

public class PalantirJavaFormatConfig {
final String version;
String groupArtifact;
String style;

PalantirJavaFormatConfig(String version) {
this.version = Objects.requireNonNull(version);
this.groupArtifact = PalantirJavaFormatStep.defaultGroupArtifact();
this.style = PalantirJavaFormatStep.defaultStyle();
addStep(createStep());
}

public PalantirJavaFormatConfig groupArtifact(String groupArtifact) {
this.groupArtifact = Objects.requireNonNull(groupArtifact);
replaceStep(createStep());
return this;
}

public PalantirJavaFormatConfig style(String style) {
this.style = Objects.requireNonNull(style);
replaceStep(createStep());
return this;
}

private FormatterStep createStep() {
return PalantirJavaFormatStep.create(
groupArtifact,
version,
style,
provisioner());
}
}

public EclipseConfig eclipse() {
return new EclipseConfig(EclipseJdtFormatterStep.defaultVersion());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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.gradle.spotless;

import java.io.IOException;

import org.junit.jupiter.api.Test;

class PalantirJavaFormatIntegrationTest extends GradleIntegrationHarness {
@Test
void integration() throws IOException {
setFile("build.gradle").toLines(
"plugins {",
" id 'com.diffplug.spotless'",
"}",
"repositories { mavenCentral() }",
"",
"spotless {",
" java {",
" target file('test.java')",
" palantirJavaFormat('1.1.0')",
" }",
"}");

setFile("test.java").toResource("java/palantirjavaformat/JavaCodeUnformatted.test");
gradleRunner().withArguments("spotlessApply").build();
assertFile("test.java").sameAsResource("java/palantirjavaformat/JavaCodeFormatted.test");

checkRunsThenUpToDate();
replace("build.gradle",
"palantirJavaFormat('1.1.0')",
"palantirJavaFormat('1.0.1')");
checkRunsThenUpToDate();
}
}
2 changes: 2 additions & 0 deletions plugin-maven/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).

## [Unreleased]
### Added
* Added support for the [palantir-java-format](https://github.com/palantir/palantir-java-format) Java formatter ([#1083](https://github.com/diffplug/spotless/pull/1083)).

## [2.19.2] - 2022-01-10
### Fixed
Expand Down
Loading