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+ * 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