Skip to content

Commit 351a637

Browse files
committed
First draft for import organization (part of clean-up).
1 parent f06c7cb commit 351a637

File tree

9 files changed

+562
-4
lines changed

9 files changed

+562
-4
lines changed

_ext/eclipse-jdt/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ ext {
1010

1111
dependencies {
1212
compile "com.diffplug.spotless:spotless-eclipse-base:${VER_SPOTLESS_ECLISPE_BASE}"
13-
compile("org.eclipse.jdt:org.eclipse.jdt.core:${VER_ECLIPSE_JDT_CORE}") {
13+
compile("org.eclipse.jdt:org.eclipse.jdt.core.manipulation:${VER_ECLIPSE_JDT_CORE_MANIPULATION}") {
14+
exclude group: 'org.eclipse.jdt', module: 'org.eclipse.jdt.launching'
1415
exclude group: 'org.eclipse.platform', module: 'org.eclipse.ant.core'
1516
exclude group: 'org.eclipse.platform', module: 'org.eclipse.core.expressions'
16-
exclude group: 'org.eclipse.platform', module: 'org.eclipse.core.filesystem'
1717
}
1818
}

_ext/eclipse-jdt/gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Mayor/Minor versions correspond to the minimum Eclipse version supported/tested.
22
# Patch version is incremented for backward compatible patches of this library.
3-
ext_version=4.8.0
3+
ext_version=4.11.0
44
ext_artifactId=spotless-eclipse-jdt
55
ext_description=Eclipse's JDT formatter bundled for Spotless
66

@@ -11,5 +11,5 @@ ext_group=com.diffplug.spotless
1111
ext_VER_JAVA=1.8
1212

1313
# Compile
14-
VER_ECLIPSE_JDT_CORE=[3.12.0,4.0.0[
14+
VER_ECLIPSE_JDT_CORE_MANIPULATION=[1.11.0,2.0.0[
1515
VER_SPOTLESS_ECLISPE_BASE=[3.0.0,4.0.0[
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright 2016 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.extra.eclipse.java;
17+
18+
import java.util.Properties;
19+
20+
import org.eclipse.core.internal.runtime.InternalPlatform;
21+
import org.eclipse.core.runtime.CoreException;
22+
import org.eclipse.core.runtime.OperationCanceledException;
23+
import org.eclipse.core.runtime.content.IContentTypeManager;
24+
import org.eclipse.core.runtime.preferences.DefaultScope;
25+
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
26+
import org.eclipse.jdt.core.ICompilationUnit;
27+
import org.eclipse.jdt.core.IJavaProject;
28+
import org.eclipse.jdt.core.JavaCore;
29+
import org.eclipse.jdt.core.dom.CompilationUnit;
30+
import org.eclipse.jdt.core.manipulation.CodeStyleConfiguration;
31+
import org.eclipse.jdt.core.manipulation.JavaManipulation;
32+
import org.eclipse.jdt.core.manipulation.OrganizeImportsOperation;
33+
import org.eclipse.jdt.core.manipulation.SharedASTProviderCore;
34+
import org.eclipse.jdt.internal.core.JavaCorePreferenceInitializer;
35+
import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettingsConstants;
36+
37+
import com.diffplug.spotless.extra.eclipse.base.SpotlessEclipseFramework;
38+
39+
/** Clean-up step which calls out to the Eclipse JDT clean-up / import sorter. */
40+
public class EclipseJdtCleanUpStepImpl {
41+
42+
/* The JDT UI shall be used for creating the settings. */
43+
private final static String JDT_UI_PLUGIN_ID = "org.eclipse.jdt.ui";
44+
45+
private final IJavaProject jdtConfiguration;
46+
47+
public EclipseJdtCleanUpStepImpl(Properties settings) throws Exception {
48+
if (SpotlessEclipseFramework.setup(
49+
core -> {
50+
/*
51+
* For the Clean-Up, the indexer needs to exists (but is not used).
52+
* The indexer is not created in headless mode by the JDT.
53+
* To signal a non-headless mode, the platform state needs to by active
54+
* (it is only resolved by default).
55+
*/
56+
core.add(new org.eclipse.core.internal.registry.osgi.Activator());
57+
core.add(new org.eclipse.core.internal.runtime.PlatformActivator());
58+
core.add(new org.eclipse.core.internal.preferences.Activator());
59+
core.add(new org.eclipse.core.internal.runtime.Activator());
60+
},
61+
config -> {
62+
config.hideEnvironment();
63+
config.disableDebugging();
64+
config.ignoreUnsupportedPreferences();
65+
config.useTemporaryLocations();
66+
config.changeSystemLineSeparator();
67+
68+
/*
69+
* The default no content type specific handling is insufficient.
70+
* The Java source type needs to be recognized by file extension.
71+
*/
72+
config.add(IContentTypeManager.class, new JavaContentTypeManager());
73+
config.useSlf4J(EclipseJdtCleanUpStepImpl.class.getPackage().getName());
74+
75+
//Initialization of jdtConfiguration requires OS set
76+
config.set(InternalPlatform.PROP_OS, "");
77+
},
78+
plugins -> {
79+
plugins.applyDefault();
80+
81+
//JDT configuration requires an existing project source folder.
82+
plugins.add(new org.eclipse.core.internal.filesystem.Activator());
83+
plugins.add(new JavaCore());
84+
})) {
85+
new JavaCorePreferenceInitializer().initializeDefaultPreferences();
86+
initializeJdtUiDefaultSettings();
87+
}
88+
jdtConfiguration = EclipseJdtFactory.createProject(settings);
89+
}
90+
91+
private static void initializeJdtUiDefaultSettings() {
92+
JavaManipulation.setPreferenceNodeId(JDT_UI_PLUGIN_ID);
93+
IEclipsePreferences prefs = DefaultScope.INSTANCE.getNode(JDT_UI_PLUGIN_ID);
94+
95+
prefs.put(CodeStyleConfiguration.ORGIMPORTS_IMPORTORDER, "java;javax;org;com");
96+
prefs.put(CodeStyleConfiguration.ORGIMPORTS_ONDEMANDTHRESHOLD, "99");
97+
prefs.put(CodeStyleConfiguration.ORGIMPORTS_STATIC_ONDEMANDTHRESHOLD, "99");
98+
99+
prefs.put(CodeGenerationSettingsConstants.CODEGEN_KEYWORD_THIS, "false");
100+
prefs.put(CodeGenerationSettingsConstants.CODEGEN_USE_OVERRIDE_ANNOTATION, "false");
101+
prefs.put(CodeGenerationSettingsConstants.CODEGEN_ADD_COMMENTS, "true");
102+
prefs.put(CodeGenerationSettingsConstants.ORGIMPORTS_IGNORELOWERCASE, "true");
103+
}
104+
105+
public String organizeImport(String raw) throws Exception {
106+
ICompilationUnit sourceContainer = EclipseJdtFactory.createJavaSource(raw, jdtConfiguration);
107+
CompilationUnit ast = SharedASTProviderCore.getAST(sourceContainer, SharedASTProviderCore.WAIT_YES, null);
108+
OrganizeImportsOperation formatOperation = new OrganizeImportsOperation(sourceContainer, ast, true, false, true, null);
109+
try {
110+
formatOperation.run(null);
111+
return sourceContainer.getSource();
112+
} catch (OperationCanceledException | CoreException e) {
113+
throw new IllegalArgumentException("Invalid java syntax for formatting.", e);
114+
}
115+
}
116+
117+
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
* Copyright 2016 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.extra.eclipse.java;
17+
18+
import java.util.Collections;
19+
import java.util.HashMap;
20+
import java.util.Hashtable;
21+
import java.util.List;
22+
import java.util.Map;
23+
import java.util.Properties;
24+
import java.util.concurrent.atomic.AtomicInteger;
25+
26+
import org.eclipse.core.internal.resources.OS;
27+
import org.eclipse.core.resources.IFolder;
28+
import org.eclipse.core.resources.IProject;
29+
import org.eclipse.core.resources.IProjectDescription;
30+
import org.eclipse.core.resources.ResourcesPlugin;
31+
import org.eclipse.core.runtime.IProgressMonitor;
32+
import org.eclipse.jdt.core.IBuffer;
33+
import org.eclipse.jdt.core.ICompilationUnit;
34+
import org.eclipse.jdt.core.IJavaProject;
35+
import org.eclipse.jdt.core.IPackageFragment;
36+
import org.eclipse.jdt.core.IPackageFragmentRoot;
37+
import org.eclipse.jdt.core.IType;
38+
import org.eclipse.jdt.core.JavaCore;
39+
import org.eclipse.jdt.core.JavaModelException;
40+
import org.eclipse.jdt.internal.core.BufferManager;
41+
import org.eclipse.jdt.internal.core.CompilationUnit;
42+
import org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner;
43+
import org.eclipse.jdt.internal.core.JavaModelManager;
44+
import org.eclipse.jdt.internal.core.PackageFragment;
45+
46+
/**
47+
* Helper methods to create Java compilation unit.
48+
* <p>
49+
* The helper provides a pseudo extension of the OS (OS specific JARs are not provided with Spotless).
50+
* The OS initialization is required for compilation unit validation
51+
* (see {@code org.eclipse.core.internal.resources.LocationValidator} for details).
52+
* </p>
53+
*/
54+
class EclipseJdtFactory extends OS {
55+
56+
private final static String ROOT_AS_SRC = "";
57+
private final static String PROJECT_NAME = "spotless";
58+
private final static String SOURCE_NAME = "source.java";
59+
private final static AtomicInteger UNIQUE_PROJECT_ID = new AtomicInteger(0);
60+
61+
private final static Map<String, String> DEFAULT_OPTIONS;
62+
63+
static {
64+
Map<String, String> defaultOptions = new HashMap<>();
65+
defaultOptions.put(JavaCore.COMPILER_SOURCE, getJavaCoreVersion());
66+
DEFAULT_OPTIONS = Collections.unmodifiableMap(defaultOptions);
67+
}
68+
69+
private static String getJavaCoreVersion() {
70+
final String javaVersion = System.getProperty("java.version");
71+
final List<String> orderedSupportedCoreVersions = JavaCore.getAllVersions();
72+
for (String coreVersion : orderedSupportedCoreVersions) {
73+
if (javaVersion.startsWith(coreVersion)) {
74+
return coreVersion;
75+
}
76+
}
77+
return orderedSupportedCoreVersions.get(orderedSupportedCoreVersions.size() - 1);
78+
}
79+
80+
/**
81+
* Creates a JAVA project and applies the configuration.
82+
* @param settings Configuration settings
83+
* @return Configured JAVA project
84+
* @throws Exception In case the project creation fails
85+
*/
86+
public final static IJavaProject createProject(Properties settings) throws Exception {
87+
String uniqueProjectName = String.format("%s-%d", PROJECT_NAME, UNIQUE_PROJECT_ID.incrementAndGet());
88+
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(uniqueProjectName);
89+
// The project must be open before items (natures, folders, sources, ...) can be created
90+
project.create(null);
91+
project.open(0, null);
92+
//If the project nature is not set, things like AST are not created for the Java projects
93+
IProjectDescription description = project.getDescription();
94+
description.setNatureIds(new String[]{JavaCore.NATURE_ID});
95+
project.setDescription(description, null);
96+
IJavaProject jProject = JavaCore.create(project);
97+
98+
Map<String, String> settingsMap = new HashMap<>(DEFAULT_OPTIONS);
99+
settings.forEach((key, value) -> {
100+
settingsMap.put(key.toString(), value.toString());
101+
});
102+
jProject.setOptions(settingsMap);
103+
104+
// Eclipse source files require an existing source folder for creation
105+
IPackageFragmentRoot src = jProject.getPackageFragmentRoot(jProject.getProject());
106+
IPackageFragment pkg = src.createPackageFragment(ROOT_AS_SRC, true, null);
107+
IFolder folder = project.getFolder(uniqueProjectName);
108+
folder.create(0, false, null);
109+
110+
// Eclipse clean-up requires an existing source file
111+
pkg.createCompilationUnit(SOURCE_NAME, "", true, null);
112+
113+
//
114+
disableSecondaryTypes(project);
115+
116+
return jProject;
117+
}
118+
119+
private static void disableSecondaryTypes(IProject project) {
120+
JavaModelManager.PerProjectInfo info = JavaModelManager.getJavaModelManager().getPerProjectInfo(project, true);
121+
info.secondaryTypes = new Hashtable<String, Map<String, IType>>();
122+
}
123+
124+
public static ICompilationUnit createJavaSource(String contents, IJavaProject jProject) throws Exception {
125+
IPackageFragmentRoot src = jProject.getPackageFragmentRoot(jProject.getProject());
126+
IPackageFragment pkg = src.getPackageFragment(ROOT_AS_SRC);
127+
return new RamCompilationUnit((PackageFragment) pkg, contents);
128+
}
129+
130+
/** Spotless keeps compilation units in RAM as long as they are worked on. */
131+
private static class RamCompilationUnit extends CompilationUnit {
132+
133+
//Each RMA compilation unit has its own buffer manager. A drop is therefore prevented.
134+
private final RamBufferManager manager;
135+
136+
RamCompilationUnit(PackageFragment parent, String contents) {
137+
super(parent, SOURCE_NAME, DefaultWorkingCopyOwner.PRIMARY);
138+
manager = new RamBufferManager();
139+
IBuffer buffer = BufferManager.createBuffer(this);
140+
buffer.setContents(contents.toCharArray());
141+
manager.add(buffer);
142+
}
143+
144+
@Override
145+
public boolean exists() {
146+
return true;
147+
}
148+
149+
@Override
150+
protected BufferManager getBufferManager() {
151+
return manager;
152+
}
153+
154+
@Override
155+
public void save(IProgressMonitor pm, boolean force) throws JavaModelException {
156+
//RAM CU is never saved to disk
157+
}
158+
159+
@Override
160+
public ICompilationUnit getWorkingCopy(IProgressMonitor monitor) throws JavaModelException {
161+
throw new UnsupportedOperationException("Spotless RAM compilation unit cannot be copied.");
162+
}
163+
164+
@Override
165+
public boolean equals(Object obj) {
166+
return this == obj; //Working copies are not supported
167+
}
168+
}
169+
170+
/** Work-around required package privileges when adding buffer for manager singleton */
171+
private static class RamBufferManager extends BufferManager {
172+
void add(IBuffer buffer) {
173+
addBuffer(buffer);
174+
}
175+
}
176+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2016 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.extra.eclipse.java;
17+
18+
import org.eclipse.core.internal.content.ContentType;
19+
import org.eclipse.core.internal.content.ContentTypeCatalog;
20+
import org.eclipse.core.runtime.content.IContentType;
21+
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
22+
23+
import com.diffplug.spotless.extra.eclipse.base.service.NoContentTypeSpecificHandling;
24+
25+
/**
26+
* Java compilation unit validation requires Java content type to be recognized.
27+
* <p>
28+
* See {@code org.eclipse.jdt.internal.core.util.Util} for details.
29+
* </p>
30+
*/
31+
public class JavaContentTypeManager extends NoContentTypeSpecificHandling {
32+
33+
private final IContentType contentType;
34+
35+
public JavaContentTypeManager() {
36+
contentType = ContentType.createContentType(
37+
new ContentTypeCatalog(null, 0),
38+
"",
39+
"",
40+
(byte) 0,
41+
new String[]{SuffixConstants.EXTENSION_java, SuffixConstants.EXTENSION_JAVA},
42+
new String[0],
43+
new String[0],
44+
"",
45+
"",
46+
null,
47+
null);
48+
}
49+
50+
@Override
51+
public IContentType getContentType(String contentTypeIdentifier) {
52+
return contentType;
53+
}
54+
55+
@Override
56+
public IContentType[] getAllContentTypes() {
57+
return new IContentType[]{contentType};
58+
}
59+
60+
}

0 commit comments

Comments
 (0)