Skip to content

Commit ced0b9e

Browse files
committed
Create new tycho-cleancode plugin
Currently there are two categories in the IDE that allows automatic code changes: 1) QuickFix that allows to resolve an error/warning automatically 2) CleanUp that allows to modernize or fix a more generic form of problem Tycho now has support to apply these automatically to a given project codebase to automate this process especially when the code evolves or new warnings occur due to changed dependencies.
1 parent ffbcbae commit ced0b9e

File tree

12 files changed

+635
-2
lines changed

12 files changed

+635
-2
lines changed

RELEASE_NOTES.md

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,62 @@ All of the following variants to specify a version are now possible:
3131
</locations>
3232
</target>
3333
```
34+
## new `quickfix` and `cleanup` mojo
35+
36+
Keeping code up-todate is a daunting task and Eclipse IDE can be a great help due to its offering for automatic quick-fixes and cleanup actions.
37+
Still this has usually be applied manually (or automatic on save) and requires some user interaction.
38+
39+
There is now a new `tycho-cleancode:cleanup` and `tycho-cleancode:quickfix` mojo that help with these things to be called via automation or part of the build,
40+
both mojos run by default in the process-sources phase so any later compilation can verify the outcome easily. Due to the way they work internally,
41+
they require an eclipse project to be present and configured currently.
42+
43+
The `tycho-cleancode:cleanup` mojo allows to configure the desired cleanup profile in the pom (maybe inside a profile), the values are those that you will find
44+
when exporting a profile from the IDE. If no profile is given the default from the project are used:
45+
46+
```xml
47+
<plugin>
48+
<groupId>org.eclipse.tycho</groupId>
49+
<artifactId>tycho-cleancode-plugin</artifactId>
50+
<version>${tycho-version}</version>
51+
<executions>
52+
<execution>
53+
<id>cleanup</id>
54+
<goals>
55+
<goal>cleanup</goal>
56+
</goals>
57+
<configuration>
58+
<cleanUpProfile>
59+
<cleanup.static_inner_class>true</cleanup.static_inner_class>
60+
</cleanUpProfile>
61+
</configuration>
62+
</execution>
63+
</executions>
64+
</plugin>
65+
```
66+
67+
The `tycho-cleancode:quickfix` mojo simply apply the quick fix with the highest relevance for resolution and can be enabled like this (maybe inside a profile):
68+
69+
```xml
70+
<plugin>
71+
<groupId>org.eclipse.tycho</groupId>
72+
<artifactId>tycho-cleancode-plugin</artifactId>
73+
<version>${tycho-version}</version>
74+
<executions>
75+
<execution>
76+
<id>quickfix</id>
77+
<goals>
78+
<goal>quickfix</goal>
79+
</goals>
80+
</execution>
81+
</executions>
82+
</plugin>
83+
```
3484

3585
## new `check-dependencies` mojo
3686

3787
When using version ranges there is a certain risk that one actually uses some methods from never release and it goes unnoticed.
3888

39-
There is now a new `tycho-baseline:dependencies mojo` that analyze the compiled class files for used method references and compares them to
89+
There is now a new `tycho-baseline:dependencies` mojo that analyze the compiled class files for used method references and compares them to
4090
the individual artifacts that match the version range. To find these versions it uses the maven metadata stored in P2 as well as
4191
the eclipse-repository index to find possible candidates.
4292

@@ -47,7 +97,7 @@ according to the discovered problems, a configuration for this might look like t
4797
<plugin>
4898
<groupId>org.eclipse.tycho</groupId>
4999
<artifactId>tycho-baseline-plugin</artifactId>
50-
<version>${tycho.version}</version>
100+
<version>${tycho-version}</version>
51101
<executions>
52102
<execution>
53103
<id>checkit</id>

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,7 @@
596596
<module>tycho-repository-plugin</module>
597597
<module>tycho-eclipse-plugin</module>
598598
<module>tycho-wrap-plugin</module>
599+
<module>tycho-cleancode-plugin</module>
599600
</modules>
600601
<profiles>
601602
<profile>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
eclipse.preferences.version=1
2+
org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
3+
org.eclipse.jdt.core.compiler.compliance=17
4+
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
5+
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
6+
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
7+
org.eclipse.jdt.core.compiler.release=enabled
8+
org.eclipse.jdt.core.compiler.source=17

tycho-cleancode-plugin/pom.xml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>org.eclipse.tycho</groupId>
7+
<artifactId>tycho</artifactId>
8+
<version>5.0.0-SNAPSHOT</version>
9+
</parent>
10+
<artifactId>tycho-cleancode-plugin</artifactId>
11+
<name>Tycho Eclipse Plugin</name>
12+
<packaging>maven-plugin</packaging>
13+
<prerequisites>
14+
<maven>${minimal-maven-version}</maven>
15+
</prerequisites>
16+
<description>Maven Plugins for performing automatic code clean options</description>
17+
<dependencies>
18+
<dependency>
19+
<groupId>org.apache.maven</groupId>
20+
<artifactId>maven-core</artifactId>
21+
</dependency>
22+
<dependency>
23+
<groupId>org.apache.maven</groupId>
24+
<artifactId>maven-plugin-api</artifactId>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.apache.maven.plugin-tools</groupId>
28+
<artifactId>maven-plugin-annotations</artifactId>
29+
</dependency>
30+
<dependency>
31+
<groupId>org.eclipse.tycho</groupId>
32+
<artifactId>tycho-eclipse-plugin</artifactId>
33+
<version>${project.version}</version>
34+
</dependency>
35+
<dependency>
36+
<groupId>org.eclipse.jdt</groupId>
37+
<artifactId>org.eclipse.jdt.ui</artifactId>
38+
<version>3.33.200</version>
39+
</dependency>
40+
<dependency>
41+
<groupId>org.eclipse.jdt</groupId>
42+
<artifactId>org.eclipse.jdt.core.manipulation</artifactId>
43+
<version>1.21.300</version>
44+
</dependency>
45+
</dependencies>
46+
47+
<build>
48+
<plugins>
49+
<plugin>
50+
<groupId>org.codehaus.plexus</groupId>
51+
<artifactId>plexus-component-metadata</artifactId>
52+
</plugin>
53+
<plugin>
54+
<groupId>org.apache.maven.plugins</groupId>
55+
<artifactId>maven-plugin-plugin</artifactId>
56+
<!-- workaround for
57+
https://issues.apache.org/jira/browse/MPLUGIN-450 -->
58+
<configuration>
59+
<goalPrefix>tycho-cleancode</goalPrefix>
60+
</configuration>
61+
</plugin>
62+
</plugins>
63+
</build>
64+
</project>
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Christoph Läubrich and others.
3+
* This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License 2.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Christoph Läubrich - initial API and implementation
12+
*******************************************************************************/
13+
package org.eclipse.tycho.cleancode;
14+
15+
import java.nio.file.Path;
16+
import java.util.ArrayList;
17+
import java.util.List;
18+
import java.util.Map;
19+
import java.util.stream.Collectors;
20+
21+
import org.eclipse.core.resources.IProject;
22+
import org.eclipse.core.resources.ProjectScope;
23+
import org.eclipse.jdt.core.ICompilationUnit;
24+
import org.eclipse.jdt.core.IJavaElement;
25+
import org.eclipse.jdt.core.IJavaProject;
26+
import org.eclipse.jdt.core.IPackageFragment;
27+
import org.eclipse.jdt.core.IPackageFragmentRoot;
28+
import org.eclipse.jdt.core.JavaCore;
29+
import org.eclipse.jdt.core.JavaModelException;
30+
import org.eclipse.jdt.internal.corext.fix.CleanUpPreferenceUtil;
31+
import org.eclipse.jdt.internal.corext.fix.CleanUpRefactoring;
32+
import org.eclipse.jdt.internal.ui.JavaPlugin;
33+
import org.eclipse.jdt.internal.ui.fix.MapCleanUpOptions;
34+
import org.eclipse.jdt.ui.cleanup.CleanUpOptions;
35+
import org.eclipse.jdt.ui.cleanup.ICleanUp;
36+
import org.eclipse.ltk.core.refactoring.Change;
37+
import org.eclipse.ltk.core.refactoring.PerformChangeOperation;
38+
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
39+
import org.eclipse.tycho.eclipsebuild.AbstractEclipseBuild;
40+
41+
public class CleanUp extends AbstractEclipseBuild<CleanupResult> {
42+
43+
private Map<String, String> customProfile;
44+
45+
CleanUp(Path projectDir, boolean debug, Map<String, String> customProfile) {
46+
super(projectDir, debug);
47+
this.customProfile = customProfile;
48+
}
49+
50+
@Override
51+
protected CleanupResult createResult(IProject project) throws Exception {
52+
CleanupResult result = new CleanupResult();
53+
CleanUpOptions options = getOptions(project);
54+
ICleanUp[] cleanups = getCleanups(result, options);
55+
if (cleanups.length > 0) {
56+
List<ICompilationUnit> units = getCompilationUnits(project);
57+
final CleanUpRefactoring refactoring = new CleanUpRefactoring(project.getName());
58+
for (ICompilationUnit cu : units) {
59+
refactoring.addCompilationUnit(cu);
60+
}
61+
refactoring.setUseOptionsFromProfile(false);
62+
for (ICleanUp cleanUp : cleanups) {
63+
refactoring.addCleanUp(cleanUp);
64+
}
65+
final RefactoringStatus status = refactoring.checkAllConditions(this);
66+
if (status.isOK()) {
67+
Change change = refactoring.createChange(this);
68+
change.initializeValidationData(this);
69+
PerformChangeOperation performChangeOperation = new PerformChangeOperation(change);
70+
performChangeOperation.run(this);
71+
} else if (status.hasError()) {
72+
throw new RuntimeException("Refactoring failed: " + status);
73+
}
74+
}
75+
return result;
76+
}
77+
78+
private List<ICompilationUnit> getCompilationUnits(IProject project) throws JavaModelException {
79+
IJavaProject javaProject = JavaCore.create(project);
80+
List<ICompilationUnit> units = new ArrayList<ICompilationUnit>();
81+
IPackageFragmentRoot[] packages = javaProject.getPackageFragmentRoots();
82+
for (IPackageFragmentRoot root : packages) {
83+
if (root.getKind() == IPackageFragmentRoot.K_SOURCE) {
84+
for (IJavaElement javaElement : root.getChildren()) {
85+
if (javaElement.getElementType() == IJavaElement.PACKAGE_FRAGMENT) {
86+
IPackageFragment pf = (IPackageFragment) javaElement;
87+
ICompilationUnit[] compilationUnits = pf.getCompilationUnits();
88+
for (ICompilationUnit compilationUnit : compilationUnits) {
89+
units.add(compilationUnit);
90+
}
91+
}
92+
}
93+
}
94+
}
95+
return units;
96+
}
97+
98+
private ICleanUp[] getCleanups(CleanupResult result, CleanUpOptions options) {
99+
ICleanUp[] cleanUps = JavaPlugin.getDefault().getCleanUpRegistry().createCleanUps();
100+
for (ICleanUp cleanUp : cleanUps) {
101+
try {
102+
cleanUp.setOptions(options);
103+
String[] descriptions = cleanUp.getStepDescriptions();
104+
if (descriptions != null) {
105+
for (String description : descriptions) {
106+
result.addCleanup(description);
107+
}
108+
}
109+
} catch (Exception e) {
110+
debug("Ignore cleanup '" + cleanUp + "' because of initialization error.", e);
111+
}
112+
}
113+
return cleanUps;
114+
}
115+
116+
private CleanUpOptions getOptions(IProject project) {
117+
Map<String, String> options;
118+
if (customProfile == null || customProfile.isEmpty()) {
119+
options = CleanUpPreferenceUtil.loadOptions(new ProjectScope(project));
120+
} else {
121+
options = customProfile;
122+
}
123+
debug("Cleanup Profile: " + options.entrySet().stream().map(e -> e.getKey() + " = " + e.getValue())
124+
.collect(Collectors.joining(System.lineSeparator())));
125+
return new MapCleanUpOptions(options);
126+
}
127+
128+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2023 Christoph Läubrich and others.
3+
* This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License 2.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Christoph Läubrich - initial API and implementation
12+
*******************************************************************************/
13+
package org.eclipse.tycho.cleancode;
14+
15+
import java.io.File;
16+
import java.io.IOException;
17+
import java.nio.file.Files;
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
import java.util.Map;
21+
import java.util.stream.Collectors;
22+
23+
import org.apache.maven.plugin.MojoFailureException;
24+
import org.apache.maven.plugins.annotations.LifecyclePhase;
25+
import org.apache.maven.plugins.annotations.Mojo;
26+
import org.apache.maven.plugins.annotations.Parameter;
27+
import org.apache.maven.plugins.annotations.ResolutionScope;
28+
import org.eclipse.tycho.eclipsebuild.AbstractEclipseBuildMojo;
29+
import org.eclipse.tycho.model.project.EclipseProject;
30+
31+
/**
32+
* This mojo allows to perform eclipse cleanup action
33+
*/
34+
@Mojo(name = "cleanup", defaultPhase = LifecyclePhase.PROCESS_SOURCES, threadSafe = true, requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME)
35+
public class CleanUpMojo extends AbstractEclipseBuildMojo<CleanupResult> {
36+
37+
@Parameter(defaultValue = "${project.build.directory}/cleanups.md", property = "tycho.cleanup.report")
38+
private File reportFileName;
39+
40+
/**
41+
* Defines key value pairs of a cleanup profile, if not defined will use the
42+
* project defaults
43+
*/
44+
@Parameter
45+
private Map<String, String> cleanUpProfile;
46+
47+
@Override
48+
protected String[] getRequireBundles() {
49+
return new String[] { "org.eclipse.jdt.ui" };
50+
}
51+
52+
@Override
53+
protected String getName() {
54+
return "Perform Cleanup";
55+
}
56+
57+
@Override
58+
protected CleanUp createExecutable() {
59+
return new CleanUp(project.getBasedir().toPath(), debug, cleanUpProfile);
60+
}
61+
62+
@Override
63+
protected void handleResult(CleanupResult result)
64+
throws MojoFailureException {
65+
List<String> results = new ArrayList<>();
66+
results.add("The following cleanups where applied:");
67+
result.cleanups().forEach(cleanup -> {
68+
results.add("- " + cleanup);
69+
});
70+
try {
71+
Files.writeString(reportFileName.toPath(),
72+
results.stream().collect(Collectors.joining(System.lineSeparator())));
73+
} catch (IOException e) {
74+
throw new MojoFailureException(e);
75+
}
76+
}
77+
78+
@Override
79+
protected boolean isValid(EclipseProject eclipseProject) {
80+
// Cleanups can only be applied to java projects
81+
return eclipseProject.hasNature("org.eclipse.jdt.core.javanature");
82+
}
83+
84+
}

0 commit comments

Comments
 (0)