From f06c7cbc129dd60e3b65819cb068165435c84dee Mon Sep 17 00:00:00 2001
From: Frank Vennemeyer
Date: Thu, 13 Jun 2019 18:14:46 +0200
Subject: [PATCH 1/6] Fixed typo in eclipse-base.
---
.../spotless/extra/eclipse/base/SpotlessEclipseFramework.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/_ext/eclipse-base/src/main/java/com/diffplug/spotless/extra/eclipse/base/SpotlessEclipseFramework.java b/_ext/eclipse-base/src/main/java/com/diffplug/spotless/extra/eclipse/base/SpotlessEclipseFramework.java
index 03e8c37d95..8be9890ac4 100644
--- a/_ext/eclipse-base/src/main/java/com/diffplug/spotless/extra/eclipse/base/SpotlessEclipseFramework.java
+++ b/_ext/eclipse-base/src/main/java/com/diffplug/spotless/extra/eclipse/base/SpotlessEclipseFramework.java
@@ -52,7 +52,7 @@ public enum DefaultBundles {
*
*
* Per default, the platform is not activated. Some plugins use this information
- * to determine whether they are running in a headless modes (without IDE).
+ * to determine whether they are running in a headless mode (without IDE).
*
*/
PLATFORM(org.eclipse.core.internal.runtime.PlatformActivator.class, Bundle.RESOLVED),
From 351a63763632ed405a5c571231760b1b492e9c10 Mon Sep 17 00:00:00 2001
From: Frank Vennemeyer
Date: Thu, 13 Jun 2019 18:18:54 +0200
Subject: [PATCH 2/6] First draft for import organization (part of clean-up).
---
_ext/eclipse-jdt/build.gradle | 4 +-
_ext/eclipse-jdt/gradle.properties | 4 +-
.../java/EclipseJdtCleanUpStepImpl.java | 117 ++++++++++++
.../extra/eclipse/java/EclipseJdtFactory.java | 176 ++++++++++++++++++
.../eclipse/java/JavaContentTypeManager.java | 60 ++++++
.../java/EclipseJdtCleanUpStepImplTest.java | 71 +++++++
.../spotless/extra/eclipse/java/TestData.java | 87 +++++++++
.../src/test/resources/Simple.input | 25 +++
.../src/test/resources/Simple.organized | 22 +++
9 files changed, 562 insertions(+), 4 deletions(-)
create mode 100644 _ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImpl.java
create mode 100644 _ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtFactory.java
create mode 100644 _ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/JavaContentTypeManager.java
create mode 100644 _ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImplTest.java
create mode 100644 _ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/TestData.java
create mode 100644 _ext/eclipse-jdt/src/test/resources/Simple.input
create mode 100644 _ext/eclipse-jdt/src/test/resources/Simple.organized
diff --git a/_ext/eclipse-jdt/build.gradle b/_ext/eclipse-jdt/build.gradle
index e079a42c41..c1eb1eb4a0 100644
--- a/_ext/eclipse-jdt/build.gradle
+++ b/_ext/eclipse-jdt/build.gradle
@@ -10,9 +10,9 @@ ext {
dependencies {
compile "com.diffplug.spotless:spotless-eclipse-base:${VER_SPOTLESS_ECLISPE_BASE}"
- compile("org.eclipse.jdt:org.eclipse.jdt.core:${VER_ECLIPSE_JDT_CORE}") {
+ compile("org.eclipse.jdt:org.eclipse.jdt.core.manipulation:${VER_ECLIPSE_JDT_CORE_MANIPULATION}") {
+ exclude group: 'org.eclipse.jdt', module: 'org.eclipse.jdt.launching'
exclude group: 'org.eclipse.platform', module: 'org.eclipse.ant.core'
exclude group: 'org.eclipse.platform', module: 'org.eclipse.core.expressions'
- exclude group: 'org.eclipse.platform', module: 'org.eclipse.core.filesystem'
}
}
\ No newline at end of file
diff --git a/_ext/eclipse-jdt/gradle.properties b/_ext/eclipse-jdt/gradle.properties
index 3b035ebd6b..4b6cbfb8f2 100644
--- a/_ext/eclipse-jdt/gradle.properties
+++ b/_ext/eclipse-jdt/gradle.properties
@@ -1,6 +1,6 @@
# Mayor/Minor versions correspond to the minimum Eclipse version supported/tested.
# Patch version is incremented for backward compatible patches of this library.
-ext_version=4.8.0
+ext_version=4.11.0
ext_artifactId=spotless-eclipse-jdt
ext_description=Eclipse's JDT formatter bundled for Spotless
@@ -11,5 +11,5 @@ ext_group=com.diffplug.spotless
ext_VER_JAVA=1.8
# Compile
-VER_ECLIPSE_JDT_CORE=[3.12.0,4.0.0[
+VER_ECLIPSE_JDT_CORE_MANIPULATION=[1.11.0,2.0.0[
VER_SPOTLESS_ECLISPE_BASE=[3.0.0,4.0.0[
diff --git a/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImpl.java b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImpl.java
new file mode 100644
index 0000000000..d94ad891ab
--- /dev/null
+++ b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImpl.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2016 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.extra.eclipse.java;
+
+import java.util.Properties;
+
+import org.eclipse.core.internal.runtime.InternalPlatform;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.content.IContentTypeManager;
+import org.eclipse.core.runtime.preferences.DefaultScope;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.manipulation.CodeStyleConfiguration;
+import org.eclipse.jdt.core.manipulation.JavaManipulation;
+import org.eclipse.jdt.core.manipulation.OrganizeImportsOperation;
+import org.eclipse.jdt.core.manipulation.SharedASTProviderCore;
+import org.eclipse.jdt.internal.core.JavaCorePreferenceInitializer;
+import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettingsConstants;
+
+import com.diffplug.spotless.extra.eclipse.base.SpotlessEclipseFramework;
+
+/** Clean-up step which calls out to the Eclipse JDT clean-up / import sorter. */
+public class EclipseJdtCleanUpStepImpl {
+
+ /* The JDT UI shall be used for creating the settings. */
+ private final static String JDT_UI_PLUGIN_ID = "org.eclipse.jdt.ui";
+
+ private final IJavaProject jdtConfiguration;
+
+ public EclipseJdtCleanUpStepImpl(Properties settings) throws Exception {
+ if (SpotlessEclipseFramework.setup(
+ core -> {
+ /*
+ * For the Clean-Up, the indexer needs to exists (but is not used).
+ * The indexer is not created in headless mode by the JDT.
+ * To signal a non-headless mode, the platform state needs to by active
+ * (it is only resolved by default).
+ */
+ core.add(new org.eclipse.core.internal.registry.osgi.Activator());
+ core.add(new org.eclipse.core.internal.runtime.PlatformActivator());
+ core.add(new org.eclipse.core.internal.preferences.Activator());
+ core.add(new org.eclipse.core.internal.runtime.Activator());
+ },
+ config -> {
+ config.hideEnvironment();
+ config.disableDebugging();
+ config.ignoreUnsupportedPreferences();
+ config.useTemporaryLocations();
+ config.changeSystemLineSeparator();
+
+ /*
+ * The default no content type specific handling is insufficient.
+ * The Java source type needs to be recognized by file extension.
+ */
+ config.add(IContentTypeManager.class, new JavaContentTypeManager());
+ config.useSlf4J(EclipseJdtCleanUpStepImpl.class.getPackage().getName());
+
+ //Initialization of jdtConfiguration requires OS set
+ config.set(InternalPlatform.PROP_OS, "");
+ },
+ plugins -> {
+ plugins.applyDefault();
+
+ //JDT configuration requires an existing project source folder.
+ plugins.add(new org.eclipse.core.internal.filesystem.Activator());
+ plugins.add(new JavaCore());
+ })) {
+ new JavaCorePreferenceInitializer().initializeDefaultPreferences();
+ initializeJdtUiDefaultSettings();
+ }
+ jdtConfiguration = EclipseJdtFactory.createProject(settings);
+ }
+
+ private static void initializeJdtUiDefaultSettings() {
+ JavaManipulation.setPreferenceNodeId(JDT_UI_PLUGIN_ID);
+ IEclipsePreferences prefs = DefaultScope.INSTANCE.getNode(JDT_UI_PLUGIN_ID);
+
+ prefs.put(CodeStyleConfiguration.ORGIMPORTS_IMPORTORDER, "java;javax;org;com");
+ prefs.put(CodeStyleConfiguration.ORGIMPORTS_ONDEMANDTHRESHOLD, "99");
+ prefs.put(CodeStyleConfiguration.ORGIMPORTS_STATIC_ONDEMANDTHRESHOLD, "99");
+
+ prefs.put(CodeGenerationSettingsConstants.CODEGEN_KEYWORD_THIS, "false");
+ prefs.put(CodeGenerationSettingsConstants.CODEGEN_USE_OVERRIDE_ANNOTATION, "false");
+ prefs.put(CodeGenerationSettingsConstants.CODEGEN_ADD_COMMENTS, "true");
+ prefs.put(CodeGenerationSettingsConstants.ORGIMPORTS_IGNORELOWERCASE, "true");
+ }
+
+ public String organizeImport(String raw) throws Exception {
+ ICompilationUnit sourceContainer = EclipseJdtFactory.createJavaSource(raw, jdtConfiguration);
+ CompilationUnit ast = SharedASTProviderCore.getAST(sourceContainer, SharedASTProviderCore.WAIT_YES, null);
+ OrganizeImportsOperation formatOperation = new OrganizeImportsOperation(sourceContainer, ast, true, false, true, null);
+ try {
+ formatOperation.run(null);
+ return sourceContainer.getSource();
+ } catch (OperationCanceledException | CoreException e) {
+ throw new IllegalArgumentException("Invalid java syntax for formatting.", e);
+ }
+ }
+
+}
diff --git a/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtFactory.java b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtFactory.java
new file mode 100644
index 0000000000..2a8e8bc534
--- /dev/null
+++ b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtFactory.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2016 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.extra.eclipse.java;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.core.internal.resources.OS;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.IBuffer;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.internal.core.BufferManager;
+import org.eclipse.jdt.internal.core.CompilationUnit;
+import org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner;
+import org.eclipse.jdt.internal.core.JavaModelManager;
+import org.eclipse.jdt.internal.core.PackageFragment;
+
+/**
+ * Helper methods to create Java compilation unit.
+ *
+ * The helper provides a pseudo extension of the OS (OS specific JARs are not provided with Spotless).
+ * The OS initialization is required for compilation unit validation
+ * (see {@code org.eclipse.core.internal.resources.LocationValidator} for details).
+ *
+ */
+class EclipseJdtFactory extends OS {
+
+ private final static String ROOT_AS_SRC = "";
+ private final static String PROJECT_NAME = "spotless";
+ private final static String SOURCE_NAME = "source.java";
+ private final static AtomicInteger UNIQUE_PROJECT_ID = new AtomicInteger(0);
+
+ private final static Map DEFAULT_OPTIONS;
+
+ static {
+ Map defaultOptions = new HashMap<>();
+ defaultOptions.put(JavaCore.COMPILER_SOURCE, getJavaCoreVersion());
+ DEFAULT_OPTIONS = Collections.unmodifiableMap(defaultOptions);
+ }
+
+ private static String getJavaCoreVersion() {
+ final String javaVersion = System.getProperty("java.version");
+ final List orderedSupportedCoreVersions = JavaCore.getAllVersions();
+ for (String coreVersion : orderedSupportedCoreVersions) {
+ if (javaVersion.startsWith(coreVersion)) {
+ return coreVersion;
+ }
+ }
+ return orderedSupportedCoreVersions.get(orderedSupportedCoreVersions.size() - 1);
+ }
+
+ /**
+ * Creates a JAVA project and applies the configuration.
+ * @param settings Configuration settings
+ * @return Configured JAVA project
+ * @throws Exception In case the project creation fails
+ */
+ public final static IJavaProject createProject(Properties settings) throws Exception {
+ String uniqueProjectName = String.format("%s-%d", PROJECT_NAME, UNIQUE_PROJECT_ID.incrementAndGet());
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(uniqueProjectName);
+ // The project must be open before items (natures, folders, sources, ...) can be created
+ project.create(null);
+ project.open(0, null);
+ //If the project nature is not set, things like AST are not created for the Java projects
+ IProjectDescription description = project.getDescription();
+ description.setNatureIds(new String[]{JavaCore.NATURE_ID});
+ project.setDescription(description, null);
+ IJavaProject jProject = JavaCore.create(project);
+
+ Map settingsMap = new HashMap<>(DEFAULT_OPTIONS);
+ settings.forEach((key, value) -> {
+ settingsMap.put(key.toString(), value.toString());
+ });
+ jProject.setOptions(settingsMap);
+
+ // Eclipse source files require an existing source folder for creation
+ IPackageFragmentRoot src = jProject.getPackageFragmentRoot(jProject.getProject());
+ IPackageFragment pkg = src.createPackageFragment(ROOT_AS_SRC, true, null);
+ IFolder folder = project.getFolder(uniqueProjectName);
+ folder.create(0, false, null);
+
+ // Eclipse clean-up requires an existing source file
+ pkg.createCompilationUnit(SOURCE_NAME, "", true, null);
+
+ //
+ disableSecondaryTypes(project);
+
+ return jProject;
+ }
+
+ private static void disableSecondaryTypes(IProject project) {
+ JavaModelManager.PerProjectInfo info = JavaModelManager.getJavaModelManager().getPerProjectInfo(project, true);
+ info.secondaryTypes = new Hashtable>();
+ }
+
+ public static ICompilationUnit createJavaSource(String contents, IJavaProject jProject) throws Exception {
+ IPackageFragmentRoot src = jProject.getPackageFragmentRoot(jProject.getProject());
+ IPackageFragment pkg = src.getPackageFragment(ROOT_AS_SRC);
+ return new RamCompilationUnit((PackageFragment) pkg, contents);
+ }
+
+ /** Spotless keeps compilation units in RAM as long as they are worked on. */
+ private static class RamCompilationUnit extends CompilationUnit {
+
+ //Each RMA compilation unit has its own buffer manager. A drop is therefore prevented.
+ private final RamBufferManager manager;
+
+ RamCompilationUnit(PackageFragment parent, String contents) {
+ super(parent, SOURCE_NAME, DefaultWorkingCopyOwner.PRIMARY);
+ manager = new RamBufferManager();
+ IBuffer buffer = BufferManager.createBuffer(this);
+ buffer.setContents(contents.toCharArray());
+ manager.add(buffer);
+ }
+
+ @Override
+ public boolean exists() {
+ return true;
+ }
+
+ @Override
+ protected BufferManager getBufferManager() {
+ return manager;
+ }
+
+ @Override
+ public void save(IProgressMonitor pm, boolean force) throws JavaModelException {
+ //RAM CU is never saved to disk
+ }
+
+ @Override
+ public ICompilationUnit getWorkingCopy(IProgressMonitor monitor) throws JavaModelException {
+ throw new UnsupportedOperationException("Spotless RAM compilation unit cannot be copied.");
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj; //Working copies are not supported
+ }
+ }
+
+ /** Work-around required package privileges when adding buffer for manager singleton */
+ private static class RamBufferManager extends BufferManager {
+ void add(IBuffer buffer) {
+ addBuffer(buffer);
+ }
+ }
+}
diff --git a/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/JavaContentTypeManager.java b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/JavaContentTypeManager.java
new file mode 100644
index 0000000000..4f16b43741
--- /dev/null
+++ b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/JavaContentTypeManager.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2016 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.extra.eclipse.java;
+
+import org.eclipse.core.internal.content.ContentType;
+import org.eclipse.core.internal.content.ContentTypeCatalog;
+import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
+
+import com.diffplug.spotless.extra.eclipse.base.service.NoContentTypeSpecificHandling;
+
+/**
+ * Java compilation unit validation requires Java content type to be recognized.
+ *
+ * See {@code org.eclipse.jdt.internal.core.util.Util} for details.
+ *
+ */
+public class JavaContentTypeManager extends NoContentTypeSpecificHandling {
+
+ private final IContentType contentType;
+
+ public JavaContentTypeManager() {
+ contentType = ContentType.createContentType(
+ new ContentTypeCatalog(null, 0),
+ "",
+ "",
+ (byte) 0,
+ new String[]{SuffixConstants.EXTENSION_java, SuffixConstants.EXTENSION_JAVA},
+ new String[0],
+ new String[0],
+ "",
+ "",
+ null,
+ null);
+ }
+
+ @Override
+ public IContentType getContentType(String contentTypeIdentifier) {
+ return contentType;
+ }
+
+ @Override
+ public IContentType[] getAllContentTypes() {
+ return new IContentType[]{contentType};
+ }
+
+}
diff --git a/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImplTest.java b/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImplTest.java
new file mode 100644
index 0000000000..14ddca94d6
--- /dev/null
+++ b/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImplTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2016 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.extra.eclipse.java;
+
+import static org.junit.Assert.*;
+
+import java.util.Properties;
+import java.util.function.Consumer;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/** Eclipse JDT wrapper integration tests */
+public class EclipseJdtCleanUpStepImplTest {
+ private static TestData TEST_DATA = null;
+
+ @BeforeClass
+ public static void initializeStatic() throws Exception {
+ TEST_DATA = TestData.getTestDataOnFileSystem();
+ }
+
+ @Test
+ public void emptyInput() throws Throwable {
+ organizeImportTest("", "", config -> {});
+ }
+
+ @Test
+ public void nominal() throws Throwable {
+ organizeImportTest("Simple", config -> {});
+ }
+
+ private static void organizeImportTest(final String fileName, final Consumer config) throws Exception {
+ organizeImportTest(TEST_DATA.input(fileName), TEST_DATA.afterOrganizedImports(fileName), config);
+ }
+
+ private static void organizeImportTest(final String input, final String expected, final Consumer config) throws Exception {
+ Properties properties = new Properties();
+ config.accept(properties);
+ EclipseJdtCleanUpStepImpl formatter = new EclipseJdtCleanUpStepImpl(properties);
+ String output = formatter.organizeImport(input);
+ assertEquals("Unexpected import organization " + toString(properties),
+ expected, output);
+ }
+
+ private static String toString(Properties properties) {
+ StringBuilder result = new StringBuilder();
+ result.append('[');
+ properties.forEach((k, v) -> {
+ result.append(k.toString());
+ result.append('=');
+ result.append(v.toString());
+ result.append(';');
+ });
+ result.append(']');
+ return result.toString();
+ }
+
+}
diff --git a/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/TestData.java b/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/TestData.java
new file mode 100644
index 0000000000..b319568896
--- /dev/null
+++ b/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/TestData.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2016 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.extra.eclipse.java;
+/*
+ * Copyright 2016 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.
+ */
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class TestData {
+ private static final String EXTENSION_INPUT = ".input";
+ private static final String EXTENSION_ORGANIZED_IMPORTS = ".organized";
+ private static final String EXTENSION_CLEAN_UP = ".cleanup";
+
+ public static TestData getTestDataOnFileSystem() {
+ final String userDir = System.getProperty("user.dir", ".");
+ Path dataPath = Paths.get(userDir, "src", "test", "resources");
+ if (Files.isDirectory(dataPath)) {
+ return new TestData(dataPath);
+ }
+ throw new IllegalArgumentException("Test data not found:" + dataPath.toString());
+ }
+
+ private final Path resourcesPath;
+
+ private TestData(Path resourcesPath) {
+ this.resourcesPath = resourcesPath.toAbsolutePath();
+ if (!Files.isDirectory(resourcesPath)) {
+ throw new IllegalArgumentException(String.format("'%1$s' is not a directory.", resourcesPath));
+ }
+ }
+
+ public String input(final String fileName) throws Exception {
+ Path filePath = resourcesPath.resolve(fileName + EXTENSION_INPUT);
+ return read(filePath);
+ }
+
+ public String afterOrganizedImports(final String fileName) {
+ Path filePath = resourcesPath.resolve(fileName + EXTENSION_ORGANIZED_IMPORTS);
+ return read(filePath);
+ }
+
+ public String afterCleanUp(final String fileName) {
+ Path filePath = resourcesPath.resolve(fileName + EXTENSION_CLEAN_UP);
+ return read(filePath);
+ }
+
+ private String read(final Path filePath) {
+ if (!Files.isRegularFile(filePath)) {
+ throw new IllegalArgumentException(String.format("'%1$s' is not a regular file.", filePath));
+ }
+ try {
+ String checkedOutFileContent = new String(java.nio.file.Files.readAllBytes(filePath), "ASCII");
+ return checkedOutFileContent.replace("\r", ""); //Align GIT end-of-line normalization
+ } catch (IOException e) {
+ throw new IllegalArgumentException(String.format("Failed to read '%1$s'.", filePath), e);
+ }
+ }
+}
diff --git a/_ext/eclipse-jdt/src/test/resources/Simple.input b/_ext/eclipse-jdt/src/test/resources/Simple.input
new file mode 100644
index 0000000000..92bbcc9c50
--- /dev/null
+++ b/_ext/eclipse-jdt/src/test/resources/Simple.input
@@ -0,0 +1,25 @@
+package bar.foo;
+
+import javax.net.SocketFactory; //Wrong order in Other group
+
+import java.net.Socket; //Wrong within group
+import java.lang.System; //Can be removed since implicitly imported
+import java.io.IOException;
+import java.io.PrintStream; //Can be found and is not used
+
+import foo.bar.C; //Cannot be found and is not used
+import foo.bar.B; //Cannot be found but is used
+
+
+class A {
+ private static final SocketFactory FACTORY;
+ static {
+ FACTORY = SocketFactory.getDefault();
+ }
+ public static Socket open() throws IOException {
+ return FACTORY.createSocket();
+ }
+ public static B create() {
+ return new B();
+ }
+}
diff --git a/_ext/eclipse-jdt/src/test/resources/Simple.organized b/_ext/eclipse-jdt/src/test/resources/Simple.organized
new file mode 100644
index 0000000000..96a92d3fbf
--- /dev/null
+++ b/_ext/eclipse-jdt/src/test/resources/Simple.organized
@@ -0,0 +1,22 @@
+package bar.foo;
+
+import java.io.IOException;
+import java.net.Socket; //Wrong within group
+
+import javax.net.SocketFactory; //Wrong order in Other group
+
+import foo.bar.B; //Cannot be found but is used
+
+
+class A {
+ private static final SocketFactory FACTORY;
+ static {
+ FACTORY = SocketFactory.getDefault();
+ }
+ public static Socket open() throws IOException {
+ return FACTORY.createSocket();
+ }
+ public static B create() {
+ return new B();
+ }
+}
From 43eea75b08466bbe5739c13308035c4ef5fdfbc3 Mon Sep 17 00:00:00 2001
From: Frank Vennemeyer
Date: Thu, 13 Jun 2019 18:20:45 +0200
Subject: [PATCH 3/6] Some JDT versions throws exception for invalid syntax
instead of returning null.
---
.../eclipse/java/EclipseJdtFormatterStepImplTest.java | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtFormatterStepImplTest.java b/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtFormatterStepImplTest.java
index d7105539a7..d3ca68fd9f 100644
--- a/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtFormatterStepImplTest.java
+++ b/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtFormatterStepImplTest.java
@@ -55,8 +55,12 @@ public void defaultFormat() throws Throwable {
@Test
public void invalidFormat() throws Throwable {
- String output = format(FORMATTED.replace("void hello() {", "void hello() "), config -> {});
- assertTrue("Incomplete Java not formatted on best effort basis.", output.contains("void hello() " + LINE_DELIMITER));
+ try {
+ String output = format(FORMATTED.replace("void hello() {", "void hello() "), config -> {});
+ assertTrue("Incomplete Java not formatted on best effort basis.", output.contains("void hello() " + LINE_DELIMITER));
+ } catch (IndexOutOfBoundsException e) {
+ //Newer JDT versions throw exception
+ }
}
@Test
From 777d0b7744a6d625570962c56ae0610baf800c32 Mon Sep 17 00:00:00 2001
From: Frank Vennemeyer
Date: Fri, 14 Jun 2019 19:27:30 +0200
Subject: [PATCH 4/6] Finalized JDT import organizer wrapper and testing.
---
.../java/EclipseJdtCleanUpStepImpl.java | 33 ++++---
...eJdtFactory.java => EclipseJdtHelper.java} | 89 ++++++++++++-------
.../eclipse/java/JavaContentTypeManager.java | 7 ++
.../java/EclipseJdtCleanUpStepImplTest.java | 41 ++++++++-
.../src/test/resources/Configuration.input | 12 +++
.../test/resources/Configuration.organized | 12 +++
.../src/test/resources/Simple.input | 6 +-
.../src/test/resources/Simple.organized | 2 +-
.../src/test/resources/Statics.input | 15 ++++
.../src/test/resources/Statics.organized | 13 +++
.../src/test/resources/Wildcards.input | 18 ++++
.../src/test/resources/Wildcards.organized | 17 ++++
12 files changed, 210 insertions(+), 55 deletions(-)
rename _ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/{EclipseJdtFactory.java => EclipseJdtHelper.java} (69%)
create mode 100644 _ext/eclipse-jdt/src/test/resources/Configuration.input
create mode 100644 _ext/eclipse-jdt/src/test/resources/Configuration.organized
create mode 100644 _ext/eclipse-jdt/src/test/resources/Statics.input
create mode 100644 _ext/eclipse-jdt/src/test/resources/Statics.organized
create mode 100644 _ext/eclipse-jdt/src/test/resources/Wildcards.input
create mode 100644 _ext/eclipse-jdt/src/test/resources/Wildcards.organized
diff --git a/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImpl.java b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImpl.java
index d94ad891ab..8125cf7a18 100644
--- a/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImpl.java
+++ b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImpl.java
@@ -31,7 +31,6 @@
import org.eclipse.jdt.core.manipulation.JavaManipulation;
import org.eclipse.jdt.core.manipulation.OrganizeImportsOperation;
import org.eclipse.jdt.core.manipulation.SharedASTProviderCore;
-import org.eclipse.jdt.internal.core.JavaCorePreferenceInitializer;
import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettingsConstants;
import com.diffplug.spotless.extra.eclipse.base.SpotlessEclipseFramework;
@@ -39,21 +38,21 @@
/** Clean-up step which calls out to the Eclipse JDT clean-up / import sorter. */
public class EclipseJdtCleanUpStepImpl {
- /* The JDT UI shall be used for creating the settings. */
+ // The JDT UI shall be used for creating the settings.
private final static String JDT_UI_PLUGIN_ID = "org.eclipse.jdt.ui";
-
- private final IJavaProject jdtConfiguration;
+ private final IJavaProject jdtConfiguration; //The project stores the JDT clean-up configuration
+ private final EclipseJdtHelper jdtHelper;
public EclipseJdtCleanUpStepImpl(Properties settings) throws Exception {
if (SpotlessEclipseFramework.setup(
core -> {
/*
- * For the Clean-Up, the indexer needs to exists (but is not used).
- * The indexer is not created in headless mode by the JDT.
- * To signal a non-headless mode, the platform state needs to by active
- * (it is only resolved by default).
+ * Indexer needs to exist (but is not used) for JDT clean-up.
+ * The indexer is not created in headless mode by JDT.
+ * 'Active' platform state signals non-headless mode ('Resolved' is default state)..
*/
core.add(new org.eclipse.core.internal.registry.osgi.Activator());
+
core.add(new org.eclipse.core.internal.runtime.PlatformActivator());
core.add(new org.eclipse.core.internal.preferences.Activator());
core.add(new org.eclipse.core.internal.runtime.Activator());
@@ -66,13 +65,12 @@ public EclipseJdtCleanUpStepImpl(Properties settings) throws Exception {
config.changeSystemLineSeparator();
/*
- * The default no content type specific handling is insufficient.
+ * The default 'no content type specific handling' is insufficient.
* The Java source type needs to be recognized by file extension.
*/
config.add(IContentTypeManager.class, new JavaContentTypeManager());
- config.useSlf4J(EclipseJdtCleanUpStepImpl.class.getPackage().getName());
- //Initialization of jdtConfiguration requires OS set
+ config.useSlf4J(EclipseJdtCleanUpStepImpl.class.getPackage().getName());
config.set(InternalPlatform.PROP_OS, "");
},
plugins -> {
@@ -82,13 +80,14 @@ public EclipseJdtCleanUpStepImpl(Properties settings) throws Exception {
plugins.add(new org.eclipse.core.internal.filesystem.Activator());
plugins.add(new JavaCore());
})) {
- new JavaCorePreferenceInitializer().initializeDefaultPreferences();
initializeJdtUiDefaultSettings();
}
- jdtConfiguration = EclipseJdtFactory.createProject(settings);
+ jdtHelper = EclipseJdtHelper.getInstance();
+ jdtConfiguration = jdtHelper.createProject(settings);
}
private static void initializeJdtUiDefaultSettings() {
+ //Following values correspond org.eclipse.jdt.ui.PreferenceConstants
JavaManipulation.setPreferenceNodeId(JDT_UI_PLUGIN_ID);
IEclipsePreferences prefs = DefaultScope.INSTANCE.getNode(JDT_UI_PLUGIN_ID);
@@ -103,12 +102,12 @@ private static void initializeJdtUiDefaultSettings() {
}
public String organizeImport(String raw) throws Exception {
- ICompilationUnit sourceContainer = EclipseJdtFactory.createJavaSource(raw, jdtConfiguration);
- CompilationUnit ast = SharedASTProviderCore.getAST(sourceContainer, SharedASTProviderCore.WAIT_YES, null);
- OrganizeImportsOperation formatOperation = new OrganizeImportsOperation(sourceContainer, ast, true, false, true, null);
+ ICompilationUnit compilationUnit = jdtHelper.createCompilationUnit(raw, jdtConfiguration);
+ CompilationUnit ast = SharedASTProviderCore.getAST(compilationUnit, SharedASTProviderCore.WAIT_YES, null);
+ OrganizeImportsOperation formatOperation = new OrganizeImportsOperation(compilationUnit, ast, true, false, true, null);
try {
formatOperation.run(null);
- return sourceContainer.getSource();
+ return compilationUnit.getSource();
} catch (OperationCanceledException | CoreException e) {
throw new IllegalArgumentException("Invalid java syntax for formatting.", e);
}
diff --git a/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtFactory.java b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtHelper.java
similarity index 69%
rename from _ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtFactory.java
rename to _ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtHelper.java
index 2a8e8bc534..e0dd62da45 100644
--- a/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtFactory.java
+++ b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtHelper.java
@@ -15,9 +15,7 @@
*/
package com.diffplug.spotless.extra.eclipse.java;
-import java.util.Collections;
import java.util.HashMap;
-import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -27,21 +25,24 @@
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
-import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.manipulation.JavaManipulation;
import org.eclipse.jdt.internal.core.BufferManager;
import org.eclipse.jdt.internal.core.CompilationUnit;
import org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner;
-import org.eclipse.jdt.internal.core.JavaModelManager;
+import org.eclipse.jdt.internal.core.JavaCorePreferenceInitializer;
import org.eclipse.jdt.internal.core.PackageFragment;
+import org.eclipse.jdt.internal.core.nd.indexer.Indexer;
/**
* Helper methods to create Java compilation unit.
@@ -51,20 +52,38 @@
* (see {@code org.eclipse.core.internal.resources.LocationValidator} for details).
*
*/
-class EclipseJdtFactory extends OS {
+class EclipseJdtHelper extends OS {
private final static String ROOT_AS_SRC = "";
private final static String PROJECT_NAME = "spotless";
private final static String SOURCE_NAME = "source.java";
- private final static AtomicInteger UNIQUE_PROJECT_ID = new AtomicInteger(0);
+ private static EclipseJdtHelper INSTANCE;
- private final static Map DEFAULT_OPTIONS;
-
- static {
- Map defaultOptions = new HashMap<>();
- defaultOptions.put(JavaCore.COMPILER_SOURCE, getJavaCoreVersion());
- DEFAULT_OPTIONS = Collections.unmodifiableMap(defaultOptions);
+ static synchronized EclipseJdtHelper getInstance() {
+ if(null == INSTANCE) {
+ INSTANCE = new EclipseJdtHelper();
+ }
+ return INSTANCE;
}
+
+ private final AtomicInteger uniqueProjectId = new AtomicInteger(0);
+ private final Map defaultOptions;
+
+ private EclipseJdtHelper() {
+ defaultOptions = new HashMap<>();
+ defaultOptions.put(JavaCore.COMPILER_SOURCE, getJavaCoreVersion());
+
+ /*
+ * Assure that the 'allowed keys' are initialized, otherwise
+ * JProject will not accept any options.
+ */
+ new JavaCorePreferenceInitializer().initializeDefaultPreferences();
+
+ /*
+ * Don't run indexer in background (does not disable thread but the job scheduling)
+ */
+ Indexer.getInstance().enableAutomaticIndexing(false);
+ }
private static String getJavaCoreVersion() {
final String javaVersion = System.getProperty("java.version");
@@ -76,30 +95,41 @@ private static String getJavaCoreVersion() {
}
return orderedSupportedCoreVersions.get(orderedSupportedCoreVersions.size() - 1);
}
-
+
/**
* Creates a JAVA project and applies the configuration.
* @param settings Configuration settings
* @return Configured JAVA project
* @throws Exception In case the project creation fails
*/
- public final static IJavaProject createProject(Properties settings) throws Exception {
- String uniqueProjectName = String.format("%s-%d", PROJECT_NAME, UNIQUE_PROJECT_ID.incrementAndGet());
+ IJavaProject createProject(Properties settings) throws Exception {
+ String uniqueProjectName = String.format("%s-%d", PROJECT_NAME, uniqueProjectId.incrementAndGet());
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(uniqueProjectName);
// The project must be open before items (natures, folders, sources, ...) can be created
project.create(null);
project.open(0, null);
- //If the project nature is not set, things like AST are not created for the Java projects
+
+ //If the project nature is not set, the AST is not created for the compilation units
IProjectDescription description = project.getDescription();
description.setNatureIds(new String[]{JavaCore.NATURE_ID});
project.setDescription(description, null);
IJavaProject jProject = JavaCore.create(project);
- Map settingsMap = new HashMap<>(DEFAULT_OPTIONS);
+ Map allSettings = new HashMap<>(defaultOptions);
settings.forEach((key, value) -> {
- settingsMap.put(key.toString(), value.toString());
+ allSettings.put(key.toString(), value.toString());
});
- jProject.setOptions(settingsMap);
+ //Configure JDT manipulation processor
+ IEclipsePreferences projectPrefs = new ProjectScope(project.getProject()).getNode(JavaManipulation.getPreferenceNodeId());
+ allSettings.forEach((key, value) -> {
+ projectPrefs.put(key.toString(), value.toString());
+ });
+ /*
+ * Configure options taken directly from the Java project (without qualifier).
+ * Whether a setting is a Java project option or not, is filtered by the
+ * JavaCorePreferenceInitializer, initialized by the constructor of this class.
+ */
+ jProject.setOptions(allSettings);
// Eclipse source files require an existing source folder for creation
IPackageFragmentRoot src = jProject.getPackageFragmentRoot(jProject.getProject());
@@ -110,27 +140,22 @@ public final static IJavaProject createProject(Properties settings) throws Excep
// Eclipse clean-up requires an existing source file
pkg.createCompilationUnit(SOURCE_NAME, "", true, null);
- //
- disableSecondaryTypes(project);
-
return jProject;
}
- private static void disableSecondaryTypes(IProject project) {
- JavaModelManager.PerProjectInfo info = JavaModelManager.getJavaModelManager().getPerProjectInfo(project, true);
- info.secondaryTypes = new Hashtable>();
- }
-
- public static ICompilationUnit createJavaSource(String contents, IJavaProject jProject) throws Exception {
+ ICompilationUnit createCompilationUnit(String contents, IJavaProject jProject) throws Exception {
IPackageFragmentRoot src = jProject.getPackageFragmentRoot(jProject.getProject());
IPackageFragment pkg = src.getPackageFragment(ROOT_AS_SRC);
return new RamCompilationUnit((PackageFragment) pkg, contents);
}
- /** Spotless keeps compilation units in RAM as long as they are worked on. */
+ /** Keep compilation units in RAM */
private static class RamCompilationUnit extends CompilationUnit {
- //Each RMA compilation unit has its own buffer manager. A drop is therefore prevented.
+ /*
+ * Each RAM compilation unit has its own buffer manager to
+ * prevent dropping of CUs when a maximum size is reached.
+ */
private final RamBufferManager manager;
RamCompilationUnit(PackageFragment parent, String contents) {
@@ -153,7 +178,7 @@ protected BufferManager getBufferManager() {
@Override
public void save(IProgressMonitor pm, boolean force) throws JavaModelException {
- //RAM CU is never saved to disk
+ //RAM CU is never stored on disk
}
@Override
@@ -167,7 +192,7 @@ public boolean equals(Object obj) {
}
}
- /** Work-around required package privileges when adding buffer for manager singleton */
+ /** Work around package privileges */
private static class RamBufferManager extends BufferManager {
void add(IBuffer buffer) {
addBuffer(buffer);
diff --git a/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/JavaContentTypeManager.java b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/JavaContentTypeManager.java
index 4f16b43741..61dc6a6a88 100644
--- a/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/JavaContentTypeManager.java
+++ b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/JavaContentTypeManager.java
@@ -18,12 +18,15 @@
import org.eclipse.core.internal.content.ContentType;
import org.eclipse.core.internal.content.ContentTypeCatalog;
import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.core.runtime.content.IContentTypeMatcher;
+import org.eclipse.core.runtime.preferences.IScopeContext;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
import com.diffplug.spotless.extra.eclipse.base.service.NoContentTypeSpecificHandling;
/**
* Java compilation unit validation requires Java content type to be recognized.
+ * All source is assumed to be Java. A content description is not required/provided.
*
* See {@code org.eclipse.jdt.internal.core.util.Util} for details.
*
@@ -57,4 +60,8 @@ public IContentType[] getAllContentTypes() {
return new IContentType[]{contentType};
}
+ @Override
+ public IContentTypeMatcher getMatcher(ISelectionPolicy customPolicy, IScopeContext context) {
+ return this;
+ }
}
diff --git a/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImplTest.java b/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImplTest.java
index 14ddca94d6..8f966335ce 100644
--- a/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImplTest.java
+++ b/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImplTest.java
@@ -20,6 +20,9 @@
import java.util.Properties;
import java.util.function.Consumer;
+import org.assertj.core.util.Arrays;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.manipulation.CodeStyleConfiguration;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -38,8 +41,42 @@ public void emptyInput() throws Throwable {
}
@Test
- public void nominal() throws Throwable {
- organizeImportTest("Simple", config -> {});
+ public void defaultConfiguration() throws Throwable {
+ for(String testFile:Arrays.array("Simple", "Statics", "Wildcards")) {
+ organizeImportTest(testFile, config -> {});
+ }
+ }
+
+ @Test
+ public void defaultPackage() throws Throwable {
+ String input = TEST_DATA.input("Simple").replaceFirst("package .+", "");
+ String expected = TEST_DATA.afterOrganizedImports("Simple").replaceFirst("package .+", "");
+ organizeImportTest(input, expected, config -> {});
+ }
+
+ @Test
+ public void invalidConfiguration() throws Throwable {
+ //Smoke test, no exceptions expected
+ organizeImportTest("", "", config -> {
+ config.put("invalid.key", "some.value");
+ });
+ organizeImportTest("", "", config -> {
+ config.put(JavaCore.COMPILER_SOURCE, "-42");
+ });
+ organizeImportTest("", "", config -> {
+ config.put(JavaCore.COMPILER_SOURCE, "Not an integer");
+ });
+ }
+
+ @Test
+ public void customConfiguration() throws Throwable {
+ String defaultOrganizedInput = TEST_DATA.input("Configuration");
+ organizeImportTest(defaultOrganizedInput, defaultOrganizedInput, config -> {});
+
+ String customOrganizedOutput = TEST_DATA.afterOrganizedImports("Configuration");
+ organizeImportTest(defaultOrganizedInput, customOrganizedOutput, config -> {
+ config.put(CodeStyleConfiguration.ORGIMPORTS_IMPORTORDER, "foo;#foo;");
+ });
}
private static void organizeImportTest(final String fileName, final Consumer config) throws Exception {
diff --git a/_ext/eclipse-jdt/src/test/resources/Configuration.input b/_ext/eclipse-jdt/src/test/resources/Configuration.input
new file mode 100644
index 0000000000..ba163e17f2
--- /dev/null
+++ b/_ext/eclipse-jdt/src/test/resources/Configuration.input
@@ -0,0 +1,12 @@
+package bar.foo;
+
+import static foo.bar.B.someMethod; //Default configuration places static imports before none-static
+
+import foo.bar.B;
+
+class A {
+ static {
+ someMethod();
+ new B();
+ }
+}
diff --git a/_ext/eclipse-jdt/src/test/resources/Configuration.organized b/_ext/eclipse-jdt/src/test/resources/Configuration.organized
new file mode 100644
index 0000000000..e51cf93fd7
--- /dev/null
+++ b/_ext/eclipse-jdt/src/test/resources/Configuration.organized
@@ -0,0 +1,12 @@
+package bar.foo;
+
+import foo.bar.B;
+
+import static foo.bar.B.someMethod; //Default configuration places static imports before none-static
+
+class A {
+ static {
+ someMethod();
+ new B();
+ }
+}
diff --git a/_ext/eclipse-jdt/src/test/resources/Simple.input b/_ext/eclipse-jdt/src/test/resources/Simple.input
index 92bbcc9c50..a837ae3d8d 100644
--- a/_ext/eclipse-jdt/src/test/resources/Simple.input
+++ b/_ext/eclipse-jdt/src/test/resources/Simple.input
@@ -5,10 +5,10 @@ import javax.net.SocketFactory; //Wrong order in Other group
import java.net.Socket; //Wrong within group
import java.lang.System; //Can be removed since implicitly imported
import java.io.IOException;
-import java.io.PrintStream; //Can be found and is not used
+import java.io.PrintStream; //Class can be found but is not used
-import foo.bar.C; //Cannot be found and is not used
-import foo.bar.B; //Cannot be found but is used
+import foo.bar.C; //Class cannot be found and is not used
+import foo.bar.B; //Class cannot be found but is used
class A {
diff --git a/_ext/eclipse-jdt/src/test/resources/Simple.organized b/_ext/eclipse-jdt/src/test/resources/Simple.organized
index 96a92d3fbf..cead7fd671 100644
--- a/_ext/eclipse-jdt/src/test/resources/Simple.organized
+++ b/_ext/eclipse-jdt/src/test/resources/Simple.organized
@@ -5,7 +5,7 @@ import java.net.Socket; //Wrong within group
import javax.net.SocketFactory; //Wrong order in Other group
-import foo.bar.B; //Cannot be found but is used
+import foo.bar.B; //Class cannot be found but is used
class A {
diff --git a/_ext/eclipse-jdt/src/test/resources/Statics.input b/_ext/eclipse-jdt/src/test/resources/Statics.input
new file mode 100644
index 0000000000..fb7e77809f
--- /dev/null
+++ b/_ext/eclipse-jdt/src/test/resources/Statics.input
@@ -0,0 +1,15 @@
+package bar.foo;
+
+import static foo.bar.B.someMethod; // Is used
+import static foo.bar.B.someOtherMethod; // Covered already by non-static import
+import static foo.bar.B.notUsed; // Is not used
+
+import foo.bar.B;
+
+class A {
+ static {
+ someMethod();
+ B.someOtherMethod();
+ new B();
+ }
+}
diff --git a/_ext/eclipse-jdt/src/test/resources/Statics.organized b/_ext/eclipse-jdt/src/test/resources/Statics.organized
new file mode 100644
index 0000000000..d88b552ff6
--- /dev/null
+++ b/_ext/eclipse-jdt/src/test/resources/Statics.organized
@@ -0,0 +1,13 @@
+package bar.foo;
+
+import static foo.bar.B.someMethod; // Is used
+
+import foo.bar.B;
+
+class A {
+ static {
+ someMethod();
+ B.someOtherMethod();
+ new B();
+ }
+}
diff --git a/_ext/eclipse-jdt/src/test/resources/Wildcards.input b/_ext/eclipse-jdt/src/test/resources/Wildcards.input
new file mode 100644
index 0000000000..3576db1b61
--- /dev/null
+++ b/_ext/eclipse-jdt/src/test/resources/Wildcards.input
@@ -0,0 +1,18 @@
+package bar.foo;
+
+import static foo.bar.C.*; //Might be used
+import static foo.bar.B.someMethod; //Is used
+import static foo.bar.B.someOtherMethod; //Is not used
+import foo.bar.B; //Is used
+import foo.bar.E; //Not used
+import hello.world.*; //Might be used
+
+class A {
+ public static B create() {
+ someMethodOfC();
+ new ClassInHelloWorld();
+
+ someMethod();
+ return new B();
+ }
+}
diff --git a/_ext/eclipse-jdt/src/test/resources/Wildcards.organized b/_ext/eclipse-jdt/src/test/resources/Wildcards.organized
new file mode 100644
index 0000000000..8f7b8ef389
--- /dev/null
+++ b/_ext/eclipse-jdt/src/test/resources/Wildcards.organized
@@ -0,0 +1,17 @@
+package bar.foo;
+
+import static foo.bar.B.someMethod; //Is used
+import static foo.bar.C.*; //Might be used
+
+import foo.bar.B; //Is used
+import hello.world.*; //Might be used
+
+class A {
+ public static B create() {
+ someMethodOfC();
+ new ClassInHelloWorld();
+
+ someMethod();
+ return new B();
+ }
+}
From 20851590091d9b0ffc8c515edcbacce1e6c3852e Mon Sep 17 00:00:00 2001
From: Frank Vennemeyer
Date: Tue, 27 Aug 2019 19:52:20 +0200
Subject: [PATCH 5/6] First draft for JDT clean-up.
---
_ext/eclipse-jdt/build.gradle | 13 +
_ext/eclipse-jdt/gradle.properties | 5 +-
.../extra/eclipse/java/CleanUpFactory.java | 216 +++++++++++++++
.../java/EclipseJdtCleanUpStepImpl.java | 262 ++++++++++++------
...r.java => EclipseJdtCoreManipulation.java} | 133 ++++++---
.../EclipseJdtOrganizeImportStepImpl.java | 47 ++++
.../java/EclipseJdtCleanUpStepImplTest.java | 65 ++---
.../EclipseJdtOrganizeImportStepImplTest.java | 99 +++++++
.../spotless/extra/eclipse/java/TestData.java | 14 +
...ration.input => ImportConfiguration.input} | 0
...rganized => ImportConfiguration.organized} | 0
11 files changed, 695 insertions(+), 159 deletions(-)
create mode 100644 _ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/CleanUpFactory.java
rename _ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/{EclipseJdtHelper.java => EclipseJdtCoreManipulation.java} (59%)
create mode 100644 _ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtOrganizeImportStepImpl.java
create mode 100644 _ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtOrganizeImportStepImplTest.java
rename _ext/eclipse-jdt/src/test/resources/{Configuration.input => ImportConfiguration.input} (100%)
rename _ext/eclipse-jdt/src/test/resources/{Configuration.organized => ImportConfiguration.organized} (100%)
diff --git a/_ext/eclipse-jdt/build.gradle b/_ext/eclipse-jdt/build.gradle
index c1eb1eb4a0..7df2c29e80 100644
--- a/_ext/eclipse-jdt/build.gradle
+++ b/_ext/eclipse-jdt/build.gradle
@@ -10,9 +10,22 @@ ext {
dependencies {
compile "com.diffplug.spotless:spotless-eclipse-base:${VER_SPOTLESS_ECLISPE_BASE}"
+ /*
+ * JDT core manipulation required for clean-up base interfaces and import sorting
+ * It depends on JDT core, which is required for fomatting.
+ */
compile("org.eclipse.jdt:org.eclipse.jdt.core.manipulation:${VER_ECLIPSE_JDT_CORE_MANIPULATION}") {
exclude group: 'org.eclipse.jdt', module: 'org.eclipse.jdt.launching'
exclude group: 'org.eclipse.platform', module: 'org.eclipse.ant.core'
exclude group: 'org.eclipse.platform', module: 'org.eclipse.core.expressions'
}
+ /*
+ * JDT UI required for clean-up.
+ * Only the org.eclipse.jdt.internal.corext.fix package is required.
+ * All dependencies (like SWT) are excluded.
+ */
+ compile("org.eclipse.jdt:org.eclipse.jdt.ui:${VER_ECLIPSE_JDT_UI}") {
+ exclude group: 'org.eclipse.platform'
+ exclude group: 'org.eclipse.jdt'
+ }
}
\ No newline at end of file
diff --git a/_ext/eclipse-jdt/gradle.properties b/_ext/eclipse-jdt/gradle.properties
index 4b6cbfb8f2..5aea1b9575 100644
--- a/_ext/eclipse-jdt/gradle.properties
+++ b/_ext/eclipse-jdt/gradle.properties
@@ -1,6 +1,6 @@
# Mayor/Minor versions correspond to the minimum Eclipse version supported/tested.
# Patch version is incremented for backward compatible patches of this library.
-ext_version=4.11.0
+ext_version=4.12.0
ext_artifactId=spotless-eclipse-jdt
ext_description=Eclipse's JDT formatter bundled for Spotless
@@ -12,4 +12,5 @@ ext_VER_JAVA=1.8
# Compile
VER_ECLIPSE_JDT_CORE_MANIPULATION=[1.11.0,2.0.0[
-VER_SPOTLESS_ECLISPE_BASE=[3.0.0,4.0.0[
+VER_ECLIPSE_JDT_UI=[3.18.0,4.0.0[
+VER_SPOTLESS_ECLISPE_BASE=[3.2.0,4.0.0[
diff --git a/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/CleanUpFactory.java b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/CleanUpFactory.java
new file mode 100644
index 0000000000..426d08d207
--- /dev/null
+++ b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/CleanUpFactory.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2016 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.extra.eclipse.java;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.xpath.XPathExpressionException;
+
+import org.assertj.core.util.Sets;
+import org.eclipse.jdt.internal.corext.fix.CleanUpConstants;
+import org.eclipse.jdt.internal.corext.fix.CleanUpConstantsOptions;
+import org.eclipse.jdt.ui.cleanup.CleanUpOptions;
+import org.eclipse.jdt.ui.cleanup.ICleanUp;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/** Provides configured clean-up implementations. */
+final class CleanUpFactory {
+
+ private final static Set UNSUPPORTED_CLASSES = Collections.unmodifiableSet(Sets.newLinkedHashSet(
+ "org.eclipse.jdt.internal.ui.fix.UnimplementedCodeCleanUp" //Would require Eclipse templates
+ ));
+
+ @SuppressWarnings("serial")
+ private final static Map UNSUPPORTED_CONFIG = Collections.unmodifiableMap(new HashMap() {
+ {
+ put(CleanUpConstants.REMOVE_UNUSED_CODE_IMPORTS, new FixedValue("false", "Unused import clean-up only works in case all imports can be resolved. As an alternative use: " + CleanUpConstants.ORGANIZE_IMPORTS));
+ }
+ });
+
+ private final static String CLEAN_UP_CONFIG_FILE_NAME = "plugin.xml";
+ private final static String CLEAN_UP_CONFIG_DEPENDENCY_NAME = "org.eclipse.jdt.ui";
+ private static List> CLEAN_UP_SEQUENCE = null;
+ private final CleanUpOptions options;
+
+ CleanUpFactory(Properties settings) {
+ options = new CleanUpOptions();
+ Logger logger = LoggerFactory.getLogger(CleanUpFactory.class);
+ CleanUpConstantsOptions.setDefaultOptions(CleanUpConstants.DEFAULT_CLEAN_UP_OPTIONS, options);
+ UNSUPPORTED_CONFIG.entrySet().stream().forEach(entry -> options.setOption(entry.getKey(), entry.getValue().value));
+ settings.forEach((key, value) -> {
+ FixedValue fixed = UNSUPPORTED_CONFIG.get(key);
+ if (null != fixed && fixed.value != value) {
+ logger.warn(String.format("Using %s for %s instead of %s: %s", fixed.value, key, value, fixed.reason));
+ } else {
+ options.setOption(key.toString(), value.toString());
+ }
+ });
+ try {
+ initializeCleanupActions();
+ } catch (IOException | ParserConfigurationException | XPathExpressionException e) {
+ throw new RuntimeException("Faild to read Eclipse Clean-Up configuration.", e);
+ }
+ }
+
+ private static synchronized void initializeCleanupActions() throws IOException, ParserConfigurationException, XPathExpressionException {
+ if (null != CLEAN_UP_SEQUENCE) {
+ return;
+ }
+ ClassLoader loader = CleanUpFactory.class.getClassLoader();
+ Optional configUrl = Collections.list(loader.getResources(CLEAN_UP_CONFIG_FILE_NAME)).stream().filter(url -> url.getPath().contains(CLEAN_UP_CONFIG_DEPENDENCY_NAME)).findAny();
+ if (!configUrl.isPresent()) {
+ throw new RuntimeException("Could not find JAR containing " + CLEAN_UP_CONFIG_DEPENDENCY_NAME + ":" + CLEAN_UP_CONFIG_FILE_NAME);
+ }
+ InputStream configXmlStream = configUrl.get().openStream();
+ try {
+ SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
+ CleanUpExtensionHandler handler = new CleanUpExtensionHandler();
+ saxParser.parse(configXmlStream, handler);
+ CLEAN_UP_SEQUENCE = handler.getCleanUpSequence();
+ } catch (SAXException e) {
+ //Add information about the XML location
+ throw new RuntimeException("Failed to parse " + configUrl.get().toExternalForm(), e);
+ }
+ }
+
+ public List create() {
+ return CLEAN_UP_SEQUENCE.stream().map(constructor -> {
+ try {
+ ICleanUp cleanUp = constructor.newInstance();
+ cleanUp.setOptions(options);
+ return cleanUp;
+ } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+ throw new RuntimeException("Failed to created clean-up action for " + constructor.getName(), e);
+ }
+ }).collect(Collectors.toList());
+ }
+
+ private static class FixedValue {
+ public final String value;
+ public final String reason;
+
+ FixedValue(String value, String reason) {
+ this.value = value;
+ this.reason = reason;
+ }
+ };
+
+ private final static class CleanUpExtensionHandler extends DefaultHandler {
+ private final static String CLEAN_UP_ELEMENT_NAME = "cleanUp";
+ private final static String ID_ATTRIBUTE_NAME = "id";
+ private final static String CLASS_ATTRIBUTE_NAME = "class";
+ private final static String RUN_AFTER_ATTRIBUTE_NAME = "runAfter";
+ private final Map> constructor;
+ private final Map runAfter;
+ private final LinkedList sorted;
+
+ CleanUpExtensionHandler() {
+ constructor = new HashMap<>();
+ runAfter = new LinkedHashMap<>(); //E.g. the elements are already sorted
+ sorted = new LinkedList<>();
+ }
+
+ @Override
+ public void startDocument() throws SAXException {
+ constructor.clear();
+ runAfter.clear();
+ sorted.clear();
+ super.startDocument();
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+ if (CLEAN_UP_ELEMENT_NAME == qName) {
+ String id = getMandatoryAttribute(attributes, ID_ATTRIBUTE_NAME);
+ String className = getMandatoryAttribute(attributes, CLASS_ATTRIBUTE_NAME);
+ if (!UNSUPPORTED_CLASSES.contains(className)) {
+ try {
+ Class> clazz = Class.forName(className);
+ Class extends ICleanUp> clazzImplementsICleanUp = clazz.asSubclass(ICleanUp.class);
+ constructor.put(id, clazzImplementsICleanUp.getConstructor());
+ } catch (ClassNotFoundException | ClassCastException | NoSuchMethodException | SecurityException e) {
+ throw new SAXException("Failed to obtain constructor for " + CLEAN_UP_ELEMENT_NAME + " element class " + className, e);
+ }
+ }
+ String runAfterId = attributes.getValue(RUN_AFTER_ATTRIBUTE_NAME);
+ if (null == runAfterId) {
+ sorted.push(id);
+ } else {
+ runAfter.put(id, runAfterId);
+ }
+ }
+ super.startElement(uri, localName, qName, attributes);
+ }
+
+ private static String getMandatoryAttribute(Attributes attributes, String qName) throws SAXException {
+ String value = attributes.getValue(qName);
+ if (null == value) {
+ throw new SAXException(CLEAN_UP_ELEMENT_NAME + " element without " + qName + " attribute.");
+ }
+ return value;
+ }
+
+ @Override
+ public void endDocument() throws SAXException {
+ if (runAfter.isEmpty()) {
+ throw new SAXException(CLEAN_UP_ELEMENT_NAME + " element has not been found in XML.");
+ }
+ while (!runAfter.isEmpty()) {
+ //E.g. the elements are already sorted. Hence only one iteration is expected.
+ List foundEntries = new ArrayList<>(runAfter.size());
+ for (Map.Entry entry : runAfter.entrySet()) {
+ int runAfterIndex = sorted.lastIndexOf(entry.getValue());
+ if (0 <= runAfterIndex) {
+ foundEntries.add(entry.getKey());
+ sorted.add(runAfterIndex + 1, entry.getKey());
+ }
+ }
+ foundEntries.forEach(e -> runAfter.remove(e));
+ if (foundEntries.isEmpty()) {
+ throw new SAXException(CLEAN_UP_ELEMENT_NAME + " element the following precessor IDs cannot be resolved: " + runAfter.values().stream().collect(Collectors.joining("; ")));
+ }
+ }
+ super.endDocument();
+ }
+
+ public List> getCleanUpSequence() {
+ return sorted.stream().map(id -> constructor.get(id)).filter(clazz -> null != clazz).collect(Collectors.toList());
+ }
+ }
+
+}
diff --git a/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImpl.java b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImpl.java
index 8125cf7a18..86fdd6a2ff 100644
--- a/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImpl.java
+++ b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImpl.java
@@ -15,102 +15,206 @@
*/
package com.diffplug.spotless.extra.eclipse.java;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
import java.util.Properties;
-import org.eclipse.core.internal.runtime.InternalPlatform;
import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.OperationCanceledException;
-import org.eclipse.core.runtime.content.IContentTypeManager;
-import org.eclipse.core.runtime.preferences.DefaultScope;
-import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
-import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.CompilationUnit;
-import org.eclipse.jdt.core.manipulation.CodeStyleConfiguration;
-import org.eclipse.jdt.core.manipulation.JavaManipulation;
-import org.eclipse.jdt.core.manipulation.OrganizeImportsOperation;
import org.eclipse.jdt.core.manipulation.SharedASTProviderCore;
-import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettingsConstants;
-
-import com.diffplug.spotless.extra.eclipse.base.SpotlessEclipseFramework;
+import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
+import org.eclipse.jdt.ui.cleanup.CleanUpContext;
+import org.eclipse.jdt.ui.cleanup.CleanUpRequirements;
+import org.eclipse.jdt.ui.cleanup.ICleanUp;
+import org.eclipse.jdt.ui.cleanup.ICleanUpFix;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.RefactoringStatusEntry;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.text.edits.UndoEdit;
/** Clean-up step which calls out to the Eclipse JDT clean-up / import sorter. */
-public class EclipseJdtCleanUpStepImpl {
+public class EclipseJdtCleanUpStepImpl extends EclipseJdtCoreManipulation {
+
+ /**
+ * In case of Eclipse JDT clean-up problems (warnings + errors)
+ * the clean-up step is skipped if not problems shall not be ignored.
+ *
+ * Value is either 'true' or 'false' ('false' per default)
+ *
+ */
+ public static final String IGNORE_CLEAN_UP_PROBLEMS = "ignoreCleanUpProblems";
- // The JDT UI shall be used for creating the settings.
- private final static String JDT_UI_PLUGIN_ID = "org.eclipse.jdt.ui";
- private final IJavaProject jdtConfiguration; //The project stores the JDT clean-up configuration
- private final EclipseJdtHelper jdtHelper;
+ private final boolean ignoreCleanUpProblems;
+ private final IJavaProject jdtConfiguration;
+ private final CleanUpFactory cleanUpFactory;
public EclipseJdtCleanUpStepImpl(Properties settings) throws Exception {
- if (SpotlessEclipseFramework.setup(
- core -> {
- /*
- * Indexer needs to exist (but is not used) for JDT clean-up.
- * The indexer is not created in headless mode by JDT.
- * 'Active' platform state signals non-headless mode ('Resolved' is default state)..
- */
- core.add(new org.eclipse.core.internal.registry.osgi.Activator());
-
- core.add(new org.eclipse.core.internal.runtime.PlatformActivator());
- core.add(new org.eclipse.core.internal.preferences.Activator());
- core.add(new org.eclipse.core.internal.runtime.Activator());
- },
- config -> {
- config.hideEnvironment();
- config.disableDebugging();
- config.ignoreUnsupportedPreferences();
- config.useTemporaryLocations();
- config.changeSystemLineSeparator();
-
- /*
- * The default 'no content type specific handling' is insufficient.
- * The Java source type needs to be recognized by file extension.
- */
- config.add(IContentTypeManager.class, new JavaContentTypeManager());
-
- config.useSlf4J(EclipseJdtCleanUpStepImpl.class.getPackage().getName());
- config.set(InternalPlatform.PROP_OS, "");
- },
- plugins -> {
- plugins.applyDefault();
-
- //JDT configuration requires an existing project source folder.
- plugins.add(new org.eclipse.core.internal.filesystem.Activator());
- plugins.add(new JavaCore());
- })) {
- initializeJdtUiDefaultSettings();
- }
- jdtHelper = EclipseJdtHelper.getInstance();
- jdtConfiguration = jdtHelper.createProject(settings);
+ jdtConfiguration = createProject(settings);
+ cleanUpFactory = new CleanUpFactory(settings);
+ ignoreCleanUpProblems = Boolean.parseBoolean(settings.getProperty(IGNORE_CLEAN_UP_PROBLEMS, "false"));
}
- private static void initializeJdtUiDefaultSettings() {
- //Following values correspond org.eclipse.jdt.ui.PreferenceConstants
- JavaManipulation.setPreferenceNodeId(JDT_UI_PLUGIN_ID);
- IEclipsePreferences prefs = DefaultScope.INSTANCE.getNode(JDT_UI_PLUGIN_ID);
+ /** Formats Java raw text. The file-location is used in log messages. */
+ public String format(String raw, String fileLocation) throws Exception {
+ ICompilationUnit compilationUnit = createCompilationUnit(raw, jdtConfiguration);
+ SpotlessRefactoring refactoring = new SpotlessRefactoring(compilationUnit, ignoreCleanUpProblems);
+ RefactoringStatus report = refactoring.apply(cleanUpFactory.create());
+ Arrays.stream(report.getEntries()).map(entry -> new SpotlessStatus(entry, fileLocation)).forEach(status -> logger.log(status));
+ return compilationUnit.getBuffer().getContents();
+ }
- prefs.put(CodeStyleConfiguration.ORGIMPORTS_IMPORTORDER, "java;javax;org;com");
- prefs.put(CodeStyleConfiguration.ORGIMPORTS_ONDEMANDTHRESHOLD, "99");
- prefs.put(CodeStyleConfiguration.ORGIMPORTS_STATIC_ONDEMANDTHRESHOLD, "99");
+ /**
+ * Spotless version of {@code org.eclipse.jdt.internal.corext.fix.CleanUpRefactoring}.
+ *
+ * Spotless does not request (graphical) user feedback neither does it provide undo-information.
+ * Since Spotless re-factoring / formatting is applied without any further explanation of the changes (preview, warnings, ...),
+ * it skips per default steps reporting problems (non-fatal errors or warnings) to ensure that the result is as expected by the user.
+ * Spotless applies the JDT re-factoring without providing a project scope (dependencies, ...).
+ * Hence steps can cause (fatal) errors which would pass within an Eclipse project.
+ * Unlike the Eclipse re-factoring process, Spotless does not abort in case a step
+ * fails, but just reports and skips the step.
+ */
+ private static class SpotlessRefactoring {
- prefs.put(CodeGenerationSettingsConstants.CODEGEN_KEYWORD_THIS, "false");
- prefs.put(CodeGenerationSettingsConstants.CODEGEN_USE_OVERRIDE_ANNOTATION, "false");
- prefs.put(CodeGenerationSettingsConstants.CODEGEN_ADD_COMMENTS, "true");
- prefs.put(CodeGenerationSettingsConstants.ORGIMPORTS_IGNORELOWERCASE, "true");
- }
+ private final ICompilationUnit source;
+ private final ICompilationUnit[] sources;
+ private final boolean ignoreProblems;
+ private final IProgressMonitor doNotMonitor;
+ private CompilationUnit lazyAst;
+ private boolean astIsFresh;
- public String organizeImport(String raw) throws Exception {
- ICompilationUnit compilationUnit = jdtHelper.createCompilationUnit(raw, jdtConfiguration);
- CompilationUnit ast = SharedASTProviderCore.getAST(compilationUnit, SharedASTProviderCore.WAIT_YES, null);
- OrganizeImportsOperation formatOperation = new OrganizeImportsOperation(compilationUnit, ast, true, false, true, null);
- try {
- formatOperation.run(null);
- return compilationUnit.getSource();
- } catch (OperationCanceledException | CoreException e) {
- throw new IllegalArgumentException("Invalid java syntax for formatting.", e);
+ SpotlessRefactoring(ICompilationUnit sourceToRefactor, boolean ignoreCleanUpProblems) {
+ source = sourceToRefactor;
+ sources = new ICompilationUnit[]{sourceToRefactor};
+ ignoreProblems = ignoreCleanUpProblems;
+ doNotMonitor = new NullProgressMonitor();
+ lazyAst = null;
+ astIsFresh = false;
+ }
+
+ RefactoringStatus apply(List steps) throws CoreException {
+ RefactoringStatus overallStatus = new RefactoringStatus();
+ for (ICleanUp step : steps) {
+ apply(step, overallStatus);
+ }
+ return overallStatus;
+ }
+
+ private void apply(ICleanUp step, RefactoringStatus overallStatus) throws CoreException {
+ RefactoringStatus preCheckStatus = step.checkPreConditions(source.getJavaProject(), sources, doNotMonitor);
+ overallStatus.merge(preCheckStatus);
+ if (isStepOk(preCheckStatus)) {
+ CleanUpContext context = createContext(step.getRequirements());
+ ICleanUpFix fix = step.createFix(context);
+ RefactoringStatus postCheckStatus = apply(step, Optional.ofNullable(fix));
+ overallStatus.merge(postCheckStatus);
+ }
+ }
+
+ private RefactoringStatus apply(ICleanUp step, Optional fix) throws CoreException {
+ RefactoringStatus postCheckStatus = new RefactoringStatus();
+ if (fix.isPresent()) {
+ CompilationUnitChange change = fix.get().createChange(doNotMonitor);
+ TextEdit edit = change.getEdit();
+ if (null != edit) {
+ UndoEdit undo = source.applyTextEdit(edit, doNotMonitor);
+ postCheckStatus = step.checkPostConditions(doNotMonitor);
+ if (isStepOk(postCheckStatus)) {
+ astIsFresh = false;
+ } else {
+ postCheckStatus.addInfo("Undo step " + step.getClass().getSimpleName());
+ if (null != undo) {
+ source.applyTextEdit(undo, doNotMonitor);
+ }
+ }
+ }
+ }
+ return postCheckStatus;
+ }
+
+ private boolean isStepOk(RefactoringStatus stepStatus) {
+ if (ignoreProblems) {
+ return stepStatus.getSeverity() < RefactoringStatus.FATAL;
+ }
+ return stepStatus.getSeverity() < RefactoringStatus.WARNING;
+ }
+
+ private CleanUpContext createContext(CleanUpRequirements requirements) {
+ if ((requirements.requiresAST() && null == lazyAst) ||
+ (requirements.requiresFreshAST() && false == astIsFresh)) {
+ lazyAst = SharedASTProviderCore.getAST(source, SharedASTProviderCore.WAIT_YES, null);
+ astIsFresh = true;
+ }
+ return new CleanUpContext(source, lazyAst);
+ }
+
+ };
+
+ private static class SpotlessStatus implements IStatus {
+ private final IStatus cleanUpStatus;
+ private final String fileLocationAsPluginId;
+
+ SpotlessStatus(RefactoringStatusEntry entry, String fileLocation) {
+ cleanUpStatus = entry.toStatus();
+ fileLocationAsPluginId = fileLocation;
+ }
+
+ @Override
+ public IStatus[] getChildren() {
+ return cleanUpStatus.getChildren();
+ }
+
+ @Override
+ public int getCode() {
+ return cleanUpStatus.getCode();
+ }
+
+ @Override
+ public Throwable getException() {
+ return cleanUpStatus.getException();
+ }
+
+ @Override
+ public String getMessage() {
+ return cleanUpStatus.getMessage();
+ }
+
+ @Override
+ public String getPlugin() {
+ /*
+ * The plugin ID of the JDT Clean-Up is always a common string.
+ * Hence it does not add any valuable information for the Spotless user.
+ * It is replaced by the file location which is hidden from the JDT re-factoring
+ * process.
+ */
+ return fileLocationAsPluginId;
+ }
+
+ @Override
+ public int getSeverity() {
+ return cleanUpStatus.getSeverity();
+ }
+
+ @Override
+ public boolean isMultiStatus() {
+ return cleanUpStatus.isMultiStatus();
+ }
+
+ @Override
+ public boolean isOK() {
+ return cleanUpStatus.isOK();
+ }
+
+ @Override
+ public boolean matches(int severityMask) {
+ return cleanUpStatus.matches(severityMask);
}
- }
+ };
}
diff --git a/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtHelper.java b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCoreManipulation.java
similarity index 59%
rename from _ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtHelper.java
rename to _ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCoreManipulation.java
index e0dd62da45..74f100fa08 100644
--- a/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtHelper.java
+++ b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCoreManipulation.java
@@ -21,13 +21,16 @@
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
-import org.eclipse.core.internal.resources.OS;
+import org.eclipse.core.internal.runtime.InternalPlatform;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.content.IContentTypeManager;
+import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.ICompilationUnit;
@@ -36,6 +39,7 @@
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.manipulation.CodeStyleConfiguration;
import org.eclipse.jdt.core.manipulation.JavaManipulation;
import org.eclipse.jdt.internal.core.BufferManager;
import org.eclipse.jdt.internal.core.CompilationUnit;
@@ -43,47 +47,96 @@
import org.eclipse.jdt.internal.core.JavaCorePreferenceInitializer;
import org.eclipse.jdt.internal.core.PackageFragment;
import org.eclipse.jdt.internal.core.nd.indexer.Indexer;
+import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettingsConstants;
+
+import com.diffplug.spotless.extra.eclipse.base.SpotlessEclipseFramework;
/**
- * Helper methods to create Java compilation unit.
- *
- * The helper provides a pseudo extension of the OS (OS specific JARs are not provided with Spotless).
- * The OS initialization is required for compilation unit validation
- * (see {@code org.eclipse.core.internal.resources.LocationValidator} for details).
- *
+ * Basic set-up for formatting features based on {@code org.eclipse.jdt:org.eclipse.jdt.core.manipulation}.
*/
-class EclipseJdtHelper extends OS {
+class EclipseJdtCoreManipulation {
+ // The JDT UI shall be used for creating the settings (JavaUI not imported due to dependencies).
+ private final static String JDT_UI_PLUGIN_ID = "org.eclipse.jdt.ui";
private final static String ROOT_AS_SRC = "";
private final static String PROJECT_NAME = "spotless";
private final static String SOURCE_NAME = "source.java";
- private static EclipseJdtHelper INSTANCE;
- static synchronized EclipseJdtHelper getInstance() {
- if(null == INSTANCE) {
- INSTANCE = new EclipseJdtHelper();
- }
- return INSTANCE;
- }
-
+ protected final ILog logger;
+
private final AtomicInteger uniqueProjectId = new AtomicInteger(0);
private final Map defaultOptions;
-
- private EclipseJdtHelper() {
+
+ protected EclipseJdtCoreManipulation() throws Exception {
+ if (SpotlessEclipseFramework.setup(
+ core -> {
+ /*
+ * Indexer needs to exist (but is not used) for JDT clean-up.
+ * The indexer is not created in headless mode by JDT.
+ * 'Active' platform state signals non-headless mode ('Resolved' is default state)..
+ */
+ core.add(new org.eclipse.core.internal.registry.osgi.Activator());
+
+ core.add(new org.eclipse.core.internal.runtime.PlatformActivator());
+ core.add(new org.eclipse.core.internal.preferences.Activator());
+ core.add(new org.eclipse.core.internal.runtime.Activator());
+ },
+ config -> {
+ config.hideEnvironment();
+ config.disableDebugging();
+ config.ignoreUnsupportedPreferences();
+ config.useTemporaryLocations();
+ config.changeSystemLineSeparator();
+
+ /*
+ * The default 'no content type specific handling' is insufficient.
+ * The Java source type needs to be recognized by file extension.
+ */
+ config.add(IContentTypeManager.class, new JavaContentTypeManager());
+
+ config.useSlf4J(EclipseJdtOrganizeImportStepImpl.class.getPackage().getName());
+ config.set(InternalPlatform.PROP_OS, ""); //Required for org.eclipse.core.internal.resources.OS initialization
+ },
+ plugins -> {
+ plugins.applyDefault();
+
+ //JDT configuration requires an existing project source folder.
+ plugins.add(new org.eclipse.core.internal.filesystem.Activator());
+ plugins.add(new JavaCore());
+ })) {
+
+ initializeJdtUiPreferenceDefaults();
+ /*
+ * Assure that the 'allowed keys' are initialized, otherwise
+ * JProject will not accept any options.
+ */
+ new JavaCorePreferenceInitializer().initializeDefaultPreferences();
+
+ /*
+ * Don't run indexer in background (does not disable thread but the job scheduling)
+ */
+ Indexer.getInstance().enableAutomaticIndexing(false);
+ }
+
defaultOptions = new HashMap<>();
defaultOptions.put(JavaCore.COMPILER_SOURCE, getJavaCoreVersion());
-
- /*
- * Assure that the 'allowed keys' are initialized, otherwise
- * JProject will not accept any options.
- */
- new JavaCorePreferenceInitializer().initializeDefaultPreferences();
-
- /*
- * Don't run indexer in background (does not disable thread but the job scheduling)
- */
- Indexer.getInstance().enableAutomaticIndexing(false);
- }
+ logger = JavaCore.getPlugin().getLog();
+ }
+
+ private static void initializeJdtUiPreferenceDefaults() {
+ //Following values correspond org.eclipse.jdt.ui.PreferenceConstants (not used due to SWT dependency)
+ JavaManipulation.setPreferenceNodeId(JDT_UI_PLUGIN_ID);
+ IEclipsePreferences prefs = DefaultScope.INSTANCE.getNode(JDT_UI_PLUGIN_ID);
+
+ prefs.put(CodeStyleConfiguration.ORGIMPORTS_IMPORTORDER, "java;javax;org;com");
+ prefs.put(CodeStyleConfiguration.ORGIMPORTS_ONDEMANDTHRESHOLD, "99");
+ prefs.put(CodeStyleConfiguration.ORGIMPORTS_STATIC_ONDEMANDTHRESHOLD, "99");
+
+ prefs.put(CodeGenerationSettingsConstants.CODEGEN_KEYWORD_THIS, "false");
+ prefs.put(CodeGenerationSettingsConstants.CODEGEN_USE_OVERRIDE_ANNOTATION, "false");
+ prefs.put(CodeGenerationSettingsConstants.CODEGEN_ADD_COMMENTS, "true");
+ prefs.put(CodeGenerationSettingsConstants.ORGIMPORTS_IGNORELOWERCASE, "true");
+ }
private static String getJavaCoreVersion() {
final String javaVersion = System.getProperty("java.version");
@@ -95,20 +148,22 @@ private static String getJavaCoreVersion() {
}
return orderedSupportedCoreVersions.get(orderedSupportedCoreVersions.size() - 1);
}
-
+
/**
* Creates a JAVA project and applies the configuration.
* @param settings Configuration settings
* @return Configured JAVA project
* @throws Exception In case the project creation fails
*/
- IJavaProject createProject(Properties settings) throws Exception {
+ protected final IJavaProject createProject(Properties settings) throws Exception {
String uniqueProjectName = String.format("%s-%d", PROJECT_NAME, uniqueProjectId.incrementAndGet());
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(uniqueProjectName);
// The project must be open before items (natures, folders, sources, ...) can be created
- project.create(null);
+ if (!project.exists()) {
+ project.create(null); //Might still exist in case of restarts and dedicated class loader
+ }
project.open(0, null);
-
+
//If the project nature is not set, the AST is not created for the compilation units
IProjectDescription description = project.getDescription();
description.setNatureIds(new String[]{JavaCore.NATURE_ID});
@@ -123,7 +178,7 @@ IJavaProject createProject(Properties settings) throws Exception {
IEclipsePreferences projectPrefs = new ProjectScope(project.getProject()).getNode(JavaManipulation.getPreferenceNodeId());
allSettings.forEach((key, value) -> {
projectPrefs.put(key.toString(), value.toString());
- });
+ });
/*
* Configure options taken directly from the Java project (without qualifier).
* Whether a setting is a Java project option or not, is filtered by the
@@ -135,7 +190,9 @@ IJavaProject createProject(Properties settings) throws Exception {
IPackageFragmentRoot src = jProject.getPackageFragmentRoot(jProject.getProject());
IPackageFragment pkg = src.createPackageFragment(ROOT_AS_SRC, true, null);
IFolder folder = project.getFolder(uniqueProjectName);
- folder.create(0, false, null);
+ if (!folder.exists()) {
+ folder.create(0, false, null);
+ }
// Eclipse clean-up requires an existing source file
pkg.createCompilationUnit(SOURCE_NAME, "", true, null);
@@ -143,7 +200,7 @@ IJavaProject createProject(Properties settings) throws Exception {
return jProject;
}
- ICompilationUnit createCompilationUnit(String contents, IJavaProject jProject) throws Exception {
+ protected final ICompilationUnit createCompilationUnit(String contents, IJavaProject jProject) throws Exception {
IPackageFragmentRoot src = jProject.getPackageFragmentRoot(jProject.getProject());
IPackageFragment pkg = src.getPackageFragment(ROOT_AS_SRC);
return new RamCompilationUnit((PackageFragment) pkg, contents);
@@ -183,7 +240,7 @@ public void save(IProgressMonitor pm, boolean force) throws JavaModelException {
@Override
public ICompilationUnit getWorkingCopy(IProgressMonitor monitor) throws JavaModelException {
- throw new UnsupportedOperationException("Spotless RAM compilation unit cannot be copied.");
+ return new RamCompilationUnit((PackageFragment) this.getParent(), getBuffer().getContents());
}
@Override
diff --git a/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtOrganizeImportStepImpl.java b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtOrganizeImportStepImpl.java
new file mode 100644
index 0000000000..f99480704c
--- /dev/null
+++ b/_ext/eclipse-jdt/src/main/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtOrganizeImportStepImpl.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2016 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.extra.eclipse.java;
+
+import java.util.Properties;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.manipulation.OrganizeImportsOperation;
+import org.eclipse.jdt.core.manipulation.SharedASTProviderCore;
+
+/** Clean-up step which calls out to the Eclipse JDT clean-up / import sorter. */
+public class EclipseJdtOrganizeImportStepImpl extends EclipseJdtCoreManipulation {
+ private final IJavaProject jdtConfiguration; //The project stores the JDT clean-up configuration
+
+ public EclipseJdtOrganizeImportStepImpl(Properties settings) throws Exception {
+ jdtConfiguration = createProject(settings);
+ }
+
+ public String format(String raw) throws Exception {
+ ICompilationUnit compilationUnit = createCompilationUnit(raw, jdtConfiguration);
+ CompilationUnit ast = SharedASTProviderCore.getAST(compilationUnit, SharedASTProviderCore.WAIT_YES, null);
+ OrganizeImportsOperation formatOperation = new OrganizeImportsOperation(compilationUnit, ast, false, false, true, null);
+ try {
+ formatOperation.run(null);
+ return compilationUnit.getSource();
+ } catch (OperationCanceledException | CoreException e) {
+ throw new IllegalArgumentException("Invalid java syntax for formatting.", e);
+ }
+ }
+}
diff --git a/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImplTest.java b/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImplTest.java
index 8f966335ce..1ebe9b92c2 100644
--- a/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImplTest.java
+++ b/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtCleanUpStepImplTest.java
@@ -24,10 +24,12 @@
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.manipulation.CodeStyleConfiguration;
import org.junit.BeforeClass;
+import org.junit.ComparisonFailure;
import org.junit.Test;
/** Eclipse JDT wrapper integration tests */
public class EclipseJdtCleanUpStepImplTest {
+ private static String SOURCE_FILE_PATH = "some .. / \\ ill&formatted/$path";
private static TestData TEST_DATA = null;
@BeforeClass
@@ -37,72 +39,55 @@ public static void initializeStatic() throws Exception {
@Test
public void emptyInput() throws Throwable {
- organizeImportTest("", "", config -> {});
+ cleanUpTest("", "", config -> {});
}
@Test
public void defaultConfiguration() throws Throwable {
- for(String testFile:Arrays.array("Simple", "Statics", "Wildcards")) {
- organizeImportTest(testFile, config -> {});
+ for (String testFile : Arrays.array("Simple", "Statics", "Wildcards")) {
+ try {
+ cleanUpTest(testFile, config -> {});
+ } catch (ComparisonFailure e) {
+ throw new ComparisonFailure(testFile + " - " + e.getMessage(), e.getExpected(), e.getActual());
+ }
}
}
-
- @Test
- public void defaultPackage() throws Throwable {
- String input = TEST_DATA.input("Simple").replaceFirst("package .+", "");
- String expected = TEST_DATA.afterOrganizedImports("Simple").replaceFirst("package .+", "");
- organizeImportTest(input, expected, config -> {});
- }
-
+
@Test
public void invalidConfiguration() throws Throwable {
//Smoke test, no exceptions expected
- organizeImportTest("", "", config -> {
+ cleanUpTest("", "", config -> {
config.put("invalid.key", "some.value");
});
- organizeImportTest("", "", config -> {
+ cleanUpTest("", "", config -> {
config.put(JavaCore.COMPILER_SOURCE, "-42");
});
- organizeImportTest("", "", config -> {
+ cleanUpTest("", "", config -> {
config.put(JavaCore.COMPILER_SOURCE, "Not an integer");
});
}
-
+
@Test
- public void customConfiguration() throws Throwable {
- String defaultOrganizedInput = TEST_DATA.input("Configuration");
- organizeImportTest(defaultOrganizedInput, defaultOrganizedInput, config -> {});
-
- String customOrganizedOutput = TEST_DATA.afterOrganizedImports("Configuration");
- organizeImportTest(defaultOrganizedInput, customOrganizedOutput, config -> {
+ public void importConfiguration() throws Throwable {
+ String defaultOrganizedInput = TEST_DATA.input("ImportConfiguration");
+ cleanUpTest(defaultOrganizedInput, defaultOrganizedInput, config -> {});
+
+ String customOrganizedOutput = TEST_DATA.afterOrganizedImports("ImportConfiguration");
+ cleanUpTest(defaultOrganizedInput, customOrganizedOutput, config -> {
config.put(CodeStyleConfiguration.ORGIMPORTS_IMPORTORDER, "foo;#foo;");
});
}
- private static void organizeImportTest(final String fileName, final Consumer config) throws Exception {
- organizeImportTest(TEST_DATA.input(fileName), TEST_DATA.afterOrganizedImports(fileName), config);
+ private static void cleanUpTest(final String fileName, final Consumer config) throws Exception {
+ cleanUpTest(TEST_DATA.input(fileName), TEST_DATA.afterCleanUp(fileName), config);
}
- private static void organizeImportTest(final String input, final String expected, final Consumer config) throws Exception {
+ private static void cleanUpTest(final String input, final String expected, final Consumer config) throws Exception {
Properties properties = new Properties();
config.accept(properties);
EclipseJdtCleanUpStepImpl formatter = new EclipseJdtCleanUpStepImpl(properties);
- String output = formatter.organizeImport(input);
- assertEquals("Unexpected import organization " + toString(properties),
+ String output = formatter.format(input, SOURCE_FILE_PATH);
+ assertEquals("Unexpected clean-up result " + TestData.toString(properties),
expected, output);
}
-
- private static String toString(Properties properties) {
- StringBuilder result = new StringBuilder();
- result.append('[');
- properties.forEach((k, v) -> {
- result.append(k.toString());
- result.append('=');
- result.append(v.toString());
- result.append(';');
- });
- result.append(']');
- return result.toString();
- }
-
}
diff --git a/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtOrganizeImportStepImplTest.java b/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtOrganizeImportStepImplTest.java
new file mode 100644
index 0000000000..fb1fcc44da
--- /dev/null
+++ b/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/EclipseJdtOrganizeImportStepImplTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2016 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.extra.eclipse.java;
+
+import static org.junit.Assert.*;
+
+import java.util.Properties;
+import java.util.function.Consumer;
+
+import org.assertj.core.util.Arrays;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.manipulation.CodeStyleConfiguration;
+import org.junit.BeforeClass;
+import org.junit.ComparisonFailure;
+import org.junit.Test;
+
+/** Eclipse JDT wrapper integration tests */
+public class EclipseJdtOrganizeImportStepImplTest {
+ private static TestData TEST_DATA = null;
+
+ @BeforeClass
+ public static void initializeStatic() throws Exception {
+ TEST_DATA = TestData.getTestDataOnFileSystem();
+ }
+
+ @Test
+ public void emptyInput() throws Throwable {
+ organizeImportTest("", "", config -> {});
+ }
+
+ @Test
+ public void defaultConfiguration() throws Throwable {
+ for (String testFile : Arrays.array("Simple", "Statics", "Wildcards")) {
+ try {
+ organizeImportTest(testFile, config -> {});
+ } catch (ComparisonFailure e) {
+ throw new ComparisonFailure(testFile + " - " + e.getMessage(), e.getExpected(), e.getActual());
+ }
+ }
+ }
+
+ @Test
+ public void defaultPackage() throws Throwable {
+ String input = TEST_DATA.input("Simple").replaceFirst("package .+", "");
+ String expected = TEST_DATA.afterOrganizedImports("Simple").replaceFirst("package .+", "");
+ organizeImportTest(input, expected, config -> {});
+ }
+
+ @Test
+ public void invalidConfiguration() throws Throwable {
+ //Smoke test, no exceptions expected
+ organizeImportTest("", "", config -> {
+ config.put("invalid.key", "some.value");
+ });
+ organizeImportTest("", "", config -> {
+ config.put(JavaCore.COMPILER_SOURCE, "-42");
+ });
+ organizeImportTest("", "", config -> {
+ config.put(JavaCore.COMPILER_SOURCE, "Not an integer");
+ });
+ }
+
+ @Test
+ public void customConfiguration() throws Throwable {
+ String defaultOrganizedInput = TEST_DATA.input("ImportConfiguration");
+ organizeImportTest(defaultOrganizedInput, defaultOrganizedInput, config -> {});
+
+ String customOrganizedOutput = TEST_DATA.afterOrganizedImports("ImportConfiguration");
+ organizeImportTest(defaultOrganizedInput, customOrganizedOutput, config -> {
+ config.put(CodeStyleConfiguration.ORGIMPORTS_IMPORTORDER, "foo;#foo;");
+ });
+ }
+
+ private static void organizeImportTest(final String fileName, final Consumer config) throws Exception {
+ organizeImportTest(TEST_DATA.input(fileName), TEST_DATA.afterOrganizedImports(fileName), config);
+ }
+
+ private static void organizeImportTest(final String input, final String expected, final Consumer config) throws Exception {
+ Properties properties = new Properties();
+ config.accept(properties);
+ EclipseJdtOrganizeImportStepImpl formatter = new EclipseJdtOrganizeImportStepImpl(properties);
+ String output = formatter.format(input);
+ assertEquals("Unexpected import organization " + TestData.toString(properties),
+ expected, output);
+ }
+}
diff --git a/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/TestData.java b/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/TestData.java
index b319568896..98b6ebae34 100644
--- a/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/TestData.java
+++ b/_ext/eclipse-jdt/src/test/java/com/diffplug/spotless/extra/eclipse/java/TestData.java
@@ -34,6 +34,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Properties;
public class TestData {
private static final String EXTENSION_INPUT = ".input";
@@ -84,4 +85,17 @@ private String read(final Path filePath) {
throw new IllegalArgumentException(String.format("Failed to read '%1$s'.", filePath), e);
}
}
+
+ public static String toString(Properties properties) {
+ StringBuilder result = new StringBuilder();
+ result.append('[');
+ properties.forEach((k, v) -> {
+ result.append(k.toString());
+ result.append('=');
+ result.append(v.toString());
+ result.append(';');
+ });
+ result.append(']');
+ return result.toString();
+ }
}
diff --git a/_ext/eclipse-jdt/src/test/resources/Configuration.input b/_ext/eclipse-jdt/src/test/resources/ImportConfiguration.input
similarity index 100%
rename from _ext/eclipse-jdt/src/test/resources/Configuration.input
rename to _ext/eclipse-jdt/src/test/resources/ImportConfiguration.input
diff --git a/_ext/eclipse-jdt/src/test/resources/Configuration.organized b/_ext/eclipse-jdt/src/test/resources/ImportConfiguration.organized
similarity index 100%
rename from _ext/eclipse-jdt/src/test/resources/Configuration.organized
rename to _ext/eclipse-jdt/src/test/resources/ImportConfiguration.organized
From b013a55cc911863cccf105da272c36545a139cdf Mon Sep 17 00:00:00 2001
From: Benoit Lacelle
Date: Mon, 13 Mar 2023 14:20:50 +0400
Subject: [PATCH 6/6] Fix some issues with new Eclipse integration
---
lib-extra/build.gradle | 20 ++++++
.../extra/glue/jdt/CleanUpFactory.java | 2 +-
.../glue/jdt/EclipseJdtCleanUpStepImpl.java | 2 +-
.../glue/jdt/EclipseJdtCoreManipulation.java | 2 +-
.../jdt/EclipseJdtOrganizeImportStepImpl.java | 2 +-
.../glue/jdt/JavaContentTypeManager.java | 2 +-
.../extra/java/EclipseJdtCleanUpStep.java | 70 +++++++++++++++++++
.../java/EclipseJdtOrganizeImportsStep.java | 70 +++++++++++++++++++
.../java/EclipseJdtCleanUpStepImplTest.java | 35 +++++++---
.../EclipseJdtOrganizeImportStepImplTest.java | 36 +++++++---
.../spotless/extra/java/TestData.java | 2 +-
11 files changed, 215 insertions(+), 28 deletions(-)
create mode 100644 lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtCleanUpStep.java
create mode 100644 lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtOrganizeImportsStep.java
diff --git a/lib-extra/build.gradle b/lib-extra/build.gradle
index 491531f0c7..4b8717f4f4 100644
--- a/lib-extra/build.gradle
+++ b/lib-extra/build.gradle
@@ -85,6 +85,26 @@ p2deps {
p2repo 'https://download.eclipse.org/tools/cdt/releases/10.7/'
install 'org.eclipse.cdt.core'
}
+
+ /*
+ * JDT core manipulation required for clean-up base interfaces and import sorting
+ * It depends on JDT core, which is required for formatting.
+ */
+ /**compile("org.eclipse.jdt:org.eclipse.jdt.core.manipulation:${VER_ECLIPSE_JDT_CORE_MANIPULATION}") {
+ exclude group: 'org.eclipse.jdt', module: 'org.eclipse.jdt.launching'
+ exclude group: 'org.eclipse.platform', module: 'org.eclipse.ant.core'
+ exclude group: 'org.eclipse.platform', module: 'org.eclipse.core.expressions'
+ }*/
+
+ /*
+ * JDT UI required for clean-up.
+ * Only the org.eclipse.jdt.internal.corext.fix package is required.
+ * All dependencies (like SWT) are excluded.
+ */
+ /**compile("org.eclipse.jdt:org.eclipse.jdt.ui:${VER_ECLIPSE_JDT_UI}") {
+ exclude group: 'org.eclipse.platform'
+ exclude group: 'org.eclipse.jdt'
+ }*/
}
// we'll hold the core lib to a high standard
diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/CleanUpFactory.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/CleanUpFactory.java
index 426d08d207..425f569363 100644
--- a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/CleanUpFactory.java
+++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/CleanUpFactory.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.diffplug.spotless.extra.eclipse.java;
+package com.diffplug.spotless.extra.glue.jdt;
import java.io.IOException;
import java.io.InputStream;
diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtCleanUpStepImpl.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtCleanUpStepImpl.java
index 86fdd6a2ff..5644f1e41e 100644
--- a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtCleanUpStepImpl.java
+++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtCleanUpStepImpl.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.diffplug.spotless.extra.eclipse.java;
+package com.diffplug.spotless.extra.glue.jdt;
import java.util.Arrays;
import java.util.List;
diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtCoreManipulation.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtCoreManipulation.java
index 74f100fa08..ec009b5f75 100644
--- a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtCoreManipulation.java
+++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtCoreManipulation.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.diffplug.spotless.extra.eclipse.java;
+package com.diffplug.spotless.extra.glue.jdt;
import java.util.HashMap;
import java.util.List;
diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtOrganizeImportStepImpl.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtOrganizeImportStepImpl.java
index f99480704c..910cfe6b89 100644
--- a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtOrganizeImportStepImpl.java
+++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtOrganizeImportStepImpl.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.diffplug.spotless.extra.eclipse.java;
+package com.diffplug.spotless.extra.glue.jdt;
import java.util.Properties;
diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/JavaContentTypeManager.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/JavaContentTypeManager.java
index 61dc6a6a88..2ddf5d4d92 100644
--- a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/JavaContentTypeManager.java
+++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/JavaContentTypeManager.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.diffplug.spotless.extra.eclipse.java;
+package com.diffplug.spotless.extra.glue.jdt;
import org.eclipse.core.internal.content.ContentType;
import org.eclipse.core.internal.content.ContentTypeCatalog;
diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtCleanUpStep.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtCleanUpStep.java
new file mode 100644
index 0000000000..95fb774676
--- /dev/null
+++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtCleanUpStep.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016-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.extra.java;
+
+import com.diffplug.spotless.FormatterFunc;
+import com.diffplug.spotless.Jvm;
+import com.diffplug.spotless.Provisioner;
+import com.diffplug.spotless.extra.EquoBasedStepBuilder;
+
+import dev.equo.solstice.p2.P2Model;
+
+import java.io.File;
+import java.util.Properties;
+
+/** Formatter step which calls out to the Eclipse JDT formatter. */
+public final class EclipseJdtCleanUpStep {
+ // prevent direct instantiation
+ private EclipseJdtCleanUpStep() {}
+
+ private static final String NAME = "eclipse jdt cleanup";
+ private static final Jvm.Support JVM_SUPPORT = Jvm. support(NAME).add(11, "4.26");
+
+ public static String defaultVersion() {
+ return JVM_SUPPORT.getRecommendedFormatterVersion();
+ }
+
+ public static EquoBasedStepBuilder createBuilder(Provisioner provisioner) {
+ return new EquoBasedStepBuilder(NAME, provisioner, EclipseJdtCleanUpStep::apply) {
+ @Override
+ protected P2Model model(String version) {
+ var model = new P2Model();
+ addPlatformRepo(model, version);
+ model.getInstall().add("org.eclipse.jdt.core");
+ return model;
+ }
+
+ @Override
+ public void setVersion(String version) {
+ if (version.endsWith(".0")) {
+ String newVersion = version.substring(0, version.length() - 2);
+ System.err.println("Recommend replacing '" + version + "' with '" + newVersion + "' for Eclipse JDT");
+ version = newVersion;
+ }
+ super.setVersion(version);
+ }
+ };
+ }
+
+ private static FormatterFunc apply(EquoBasedStepBuilder.State state) throws Exception {
+ JVM_SUPPORT.assertFormatterSupported(state.getSemanticVersion());
+ Class> formatterClazz = state.getJarState().getClassLoader().loadClass("com.diffplug.spotless.extra.glue.jdt.EclipseJdtCleanUpStepImpl");
+ var formatter = formatterClazz.getConstructor(Properties.class).newInstance(state.getPreferences());
+ var method = formatterClazz.getMethod("format", String.class, File.class);
+ FormatterFunc formatterFunc = (FormatterFunc.NeedsFile) (input, file) -> (String) method.invoke(formatter, input, file);
+ return JVM_SUPPORT.suggestLaterVersionOnError(state.getSemanticVersion(), formatterFunc);
+ }
+}
diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtOrganizeImportsStep.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtOrganizeImportsStep.java
new file mode 100644
index 0000000000..ed446f5378
--- /dev/null
+++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtOrganizeImportsStep.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016-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.extra.java;
+
+import com.diffplug.spotless.FormatterFunc;
+import com.diffplug.spotless.Jvm;
+import com.diffplug.spotless.Provisioner;
+import com.diffplug.spotless.extra.EquoBasedStepBuilder;
+
+import dev.equo.solstice.p2.P2Model;
+
+import java.io.File;
+import java.util.Properties;
+
+/** Formatter step which calls out to the Eclipse JDT formatter. */
+public final class EclipseJdtOrganizeImportsStep {
+ // prevent direct instantiation
+ private EclipseJdtOrganizeImportsStep() {}
+
+ private static final String NAME = "eclipse jdt organizeImports";
+ private static final Jvm.Support JVM_SUPPORT = Jvm. support(NAME).add(11, "4.26");
+
+ public static String defaultVersion() {
+ return JVM_SUPPORT.getRecommendedFormatterVersion();
+ }
+
+ public static EquoBasedStepBuilder createBuilder(Provisioner provisioner) {
+ return new EquoBasedStepBuilder(NAME, provisioner, EclipseJdtOrganizeImportsStep::apply) {
+ @Override
+ protected P2Model model(String version) {
+ var model = new P2Model();
+ addPlatformRepo(model, version);
+ model.getInstall().add("org.eclipse.jdt.core");
+ return model;
+ }
+
+ @Override
+ public void setVersion(String version) {
+ if (version.endsWith(".0")) {
+ String newVersion = version.substring(0, version.length() - 2);
+ System.err.println("Recommend replacing '" + version + "' with '" + newVersion + "' for Eclipse JDT");
+ version = newVersion;
+ }
+ super.setVersion(version);
+ }
+ };
+ }
+
+ private static FormatterFunc apply(EquoBasedStepBuilder.State state) throws Exception {
+ JVM_SUPPORT.assertFormatterSupported(state.getSemanticVersion());
+ Class> formatterClazz = state.getJarState().getClassLoader().loadClass("com.diffplug.spotless.extra.glue.jdt.EclipseJdtOrganizeImportsStepImpl");
+ var formatter = formatterClazz.getConstructor(Properties.class).newInstance(state.getPreferences());
+ var method = formatterClazz.getMethod("format", String.class, File.class);
+ FormatterFunc formatterFunc = (FormatterFunc.NeedsFile) (input, file) -> (String) method.invoke(formatter, input, file);
+ return JVM_SUPPORT.suggestLaterVersionOnError(state.getSemanticVersion(), formatterFunc);
+ }
+}
diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtCleanUpStepImplTest.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtCleanUpStepImplTest.java
index 1ebe9b92c2..7805537a6c 100644
--- a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtCleanUpStepImplTest.java
+++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtCleanUpStepImplTest.java
@@ -13,30 +13,43 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.diffplug.spotless.extra.eclipse.java;
-
-import static org.junit.Assert.*;
+package com.diffplug.spotless.extra.java;
import java.util.Properties;
import java.util.function.Consumer;
+import com.diffplug.spotless.TestProvisioner;
+import com.diffplug.spotless.extra.EquoBasedStepBuilder;
+import com.diffplug.spotless.extra.eclipse.EquoResourceHarness;
+
import org.assertj.core.util.Arrays;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.manipulation.CodeStyleConfiguration;
-import org.junit.BeforeClass;
-import org.junit.ComparisonFailure;
-import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.opentest4j.AssertionFailedError;
/** Eclipse JDT wrapper integration tests */
-public class EclipseJdtCleanUpStepImplTest {
+public class EclipseJdtCleanUpStepImplTest extends EquoResourceHarness {
+ private final static String INPUT = "package p; class C{}";
+ private final static String EXPECTED = "package p;\nclass C {\n}";
private static String SOURCE_FILE_PATH = "some .. / \\ ill&formatted/$path";
private static TestData TEST_DATA = null;
- @BeforeClass
+ @BeforeAll
public static void initializeStatic() throws Exception {
TEST_DATA = TestData.getTestDataOnFileSystem();
}
+ private static EquoBasedStepBuilder createBuilder() {
+ return EclipseJdtCleanUpStep.createBuilder(TestProvisioner.mavenCentral());
+ }
+
+ public EclipseJdtCleanUpStepImplTest() {
+ super(createBuilder(), INPUT, EXPECTED);
+ }
+
@Test
public void emptyInput() throws Throwable {
cleanUpTest("", "", config -> {});
@@ -47,8 +60,8 @@ public void defaultConfiguration() throws Throwable {
for (String testFile : Arrays.array("Simple", "Statics", "Wildcards")) {
try {
cleanUpTest(testFile, config -> {});
- } catch (ComparisonFailure e) {
- throw new ComparisonFailure(testFile + " - " + e.getMessage(), e.getExpected(), e.getActual());
+ } catch (AssertionFailedError e) {
+ throw new AssertionFailedError(testFile + " - " + e.getMessage(), e.getExpected(), e.getActual());
}
}
}
@@ -87,7 +100,7 @@ private static void cleanUpTest(final String input, final String expected, final
config.accept(properties);
EclipseJdtCleanUpStepImpl formatter = new EclipseJdtCleanUpStepImpl(properties);
String output = formatter.format(input, SOURCE_FILE_PATH);
- assertEquals("Unexpected clean-up result " + TestData.toString(properties),
+ Assertions.assertEquals("Unexpected clean-up result " + TestData.toString(properties),
expected, output);
}
}
diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtOrganizeImportStepImplTest.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtOrganizeImportStepImplTest.java
index fb1fcc44da..8b23adf7ce 100644
--- a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtOrganizeImportStepImplTest.java
+++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtOrganizeImportStepImplTest.java
@@ -13,29 +13,43 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.diffplug.spotless.extra.eclipse.java;
-
-import static org.junit.Assert.*;
+package com.diffplug.spotless.extra.java;
import java.util.Properties;
import java.util.function.Consumer;
+import com.diffplug.spotless.TestProvisioner;
+import com.diffplug.spotless.extra.EquoBasedStepBuilder;
+import com.diffplug.spotless.extra.eclipse.EquoResourceHarness;
+
import org.assertj.core.util.Arrays;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.manipulation.CodeStyleConfiguration;
-import org.junit.BeforeClass;
-import org.junit.ComparisonFailure;
-import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.opentest4j.AssertionFailedError;
/** Eclipse JDT wrapper integration tests */
-public class EclipseJdtOrganizeImportStepImplTest {
+public class EclipseJdtOrganizeImportStepImplTest extends EquoResourceHarness {
+ private final static String INPUT = "package p; class C{}";
+ private final static String EXPECTED = "package p;\nclass C {\n}";
+
private static TestData TEST_DATA = null;
- @BeforeClass
+ @BeforeAll
public static void initializeStatic() throws Exception {
TEST_DATA = TestData.getTestDataOnFileSystem();
}
+ private static EquoBasedStepBuilder createBuilder() {
+ return EclipseJdtOrganizeImportStep.createBuilder(TestProvisioner.mavenCentral());
+ }
+
+ public EclipseJdtOrganizeImportStepImplTest() {
+ super(createBuilder(), INPUT, EXPECTED);
+ }
+
@Test
public void emptyInput() throws Throwable {
organizeImportTest("", "", config -> {});
@@ -46,8 +60,8 @@ public void defaultConfiguration() throws Throwable {
for (String testFile : Arrays.array("Simple", "Statics", "Wildcards")) {
try {
organizeImportTest(testFile, config -> {});
- } catch (ComparisonFailure e) {
- throw new ComparisonFailure(testFile + " - " + e.getMessage(), e.getExpected(), e.getActual());
+ } catch (AssertionFailedError e) {
+ throw new AssertionFailedError(testFile + " - " + e.getMessage(), e.getExpected(), e.getActual());
}
}
}
@@ -93,7 +107,7 @@ private static void organizeImportTest(final String input, final String expected
config.accept(properties);
EclipseJdtOrganizeImportStepImpl formatter = new EclipseJdtOrganizeImportStepImpl(properties);
String output = formatter.format(input);
- assertEquals("Unexpected import organization " + TestData.toString(properties),
+ Assertions.assertEquals("Unexpected import organization " + TestData.toString(properties),
expected, output);
}
}
diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/TestData.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/TestData.java
index 98b6ebae34..db6aaa5b72 100644
--- a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/TestData.java
+++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/TestData.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.diffplug.spotless.extra.eclipse.java;
+package com.diffplug.spotless.extra.java;
/*
* Copyright 2016 DiffPlug
*