Skip to content

Commit

Permalink
Create new tycho-cleancode plugin
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
laeubi committed Jan 21, 2025
1 parent fdc0adf commit 60d33e0
Show file tree
Hide file tree
Showing 10 changed files with 478 additions and 0 deletions.
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,7 @@
<module>tycho-repository-plugin</module>
<module>tycho-eclipse-plugin</module>
<module>tycho-wrap-plugin</module>
<module>tycho-cleancode-plugin</module>
</modules>
<profiles>
<profile>
Expand Down
8 changes: 8 additions & 0 deletions tycho-cleancode-plugin/.settings/org.eclipse.jdt.core.prefs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
org.eclipse.jdt.core.compiler.compliance=17
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=17
64 changes: 64 additions & 0 deletions tycho-cleancode-plugin/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho</artifactId>
<version>5.0.0-SNAPSHOT</version>
</parent>
<artifactId>tycho-cleancode-plugin</artifactId>
<name>Tycho Eclipse Plugin</name>
<packaging>maven-plugin</packaging>
<prerequisites>
<maven>${minimal-maven-version}</maven>
</prerequisites>
<description>Maven Plugins for performing automatic code clean options</description>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-eclipse-plugin</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>org.eclipse.jdt.ui</artifactId>
<version>3.33.200</version>
</dependency>
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>org.eclipse.jdt.core.manipulation</artifactId>
<version>1.21.300</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-component-metadata</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<!-- workaround for
https://issues.apache.org/jira/browse/MPLUGIN-450 -->
<configuration>
<goalPrefix>tycho-cleancode</goalPrefix>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*******************************************************************************
* Copyright (c) 2025 Christoph Läubrich and others.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Christoph Läubrich - initial API and implementation
*******************************************************************************/
package org.eclipse.tycho.cleancode;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.corext.fix.CleanUpPreferenceUtil;
import org.eclipse.jdt.internal.corext.fix.CleanUpRefactoring;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.fix.MapCleanUpOptions;
import org.eclipse.jdt.ui.cleanup.CleanUpOptions;
import org.eclipse.jdt.ui.cleanup.ICleanUp;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.PerformChangeOperation;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.tycho.eclipsebuild.AbstractEclipseBuild;

public class CleanUp extends AbstractEclipseBuild<CleanupResult> {

private Map<String, String> customProfile;

CleanUp(Path projectDir, boolean debug, Map<String, String> customProfile) {
super(projectDir, debug);
this.customProfile = customProfile;
}

@Override
protected CleanupResult createResult(IProject project) throws Exception {
CleanupResult result = new CleanupResult();
CleanUpOptions options = getOptions(project);
ICleanUp[] cleanups = getCleanups(result, options);
if (cleanups.length > 0) {
List<ICompilationUnit> units = getCompilationUnits(project);
final CleanUpRefactoring refactoring = new CleanUpRefactoring(project.getName());
for (ICompilationUnit cu : units) {
refactoring.addCompilationUnit(cu);
}
refactoring.setUseOptionsFromProfile(false);
for (ICleanUp cleanUp : cleanups) {
refactoring.addCleanUp(cleanUp);
}
final RefactoringStatus status = refactoring.checkAllConditions(this);
if (status.isOK()) {
Change change = refactoring.createChange(this);
change.initializeValidationData(this);
PerformChangeOperation performChangeOperation = new PerformChangeOperation(change);
performChangeOperation.run(this);
} else if (status.hasError()) {
throw new RuntimeException("Refactoring failed: " + status);
}
}
return result;
}

private List<ICompilationUnit> getCompilationUnits(IProject project) throws JavaModelException {
IJavaProject javaProject = JavaCore.create(project);
List<ICompilationUnit> units = new ArrayList<ICompilationUnit>();
IPackageFragmentRoot[] packages = javaProject.getPackageFragmentRoots();
for (IPackageFragmentRoot root : packages) {
if (root.getKind() == IPackageFragmentRoot.K_SOURCE) {
for (IJavaElement javaElement : root.getChildren()) {
if (javaElement.getElementType() == IJavaElement.PACKAGE_FRAGMENT) {
IPackageFragment pf = (IPackageFragment) javaElement;
ICompilationUnit[] compilationUnits = pf.getCompilationUnits();
for (ICompilationUnit compilationUnit : compilationUnits) {
units.add(compilationUnit);
}
}
}
}
}
return units;
}

private ICleanUp[] getCleanups(CleanupResult result, CleanUpOptions options) {
ICleanUp[] cleanUps = JavaPlugin.getDefault().getCleanUpRegistry().createCleanUps();
for (ICleanUp cleanUp : cleanUps) {
try {
cleanUp.setOptions(options);
String[] descriptions = cleanUp.getStepDescriptions();
if (descriptions != null) {
for (String description : descriptions) {
result.addCleanup(description);
}
}
} catch (Exception e) {
debug("Ignore cleanup '" + cleanUp + "' because of initialization error.", e);
}
}
return cleanUps;
}

private CleanUpOptions getOptions(IProject project) {
Map<String, String> options;
if (customProfile == null || customProfile.isEmpty()) {
options = CleanUpPreferenceUtil.loadOptions(new ProjectScope(project));
} else {
options = customProfile;
}
debug("Cleanup Profile: " + options.entrySet().stream().map(e -> e.getKey() + " = " + e.getValue())
.collect(Collectors.joining(System.lineSeparator())));
return new MapCleanUpOptions(options);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*******************************************************************************
* Copyright (c) 2023 Christoph Läubrich and others.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Christoph Läubrich - initial API and implementation
*******************************************************************************/
package org.eclipse.tycho.cleancode;

import java.util.Map;

import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.eclipse.tycho.eclipsebuild.AbstractEclipseBuildMojo;

/**
* This mojo allows to perform eclipse cleanup action
*/
@Mojo(name = "cleanup", defaultPhase = LifecyclePhase.PROCESS_SOURCES, threadSafe = true, requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME)
public class CleanUpMojo extends AbstractEclipseBuildMojo<CleanupResult> {

/**
* Defines key value pairs of a cleanup profile, if not defined will use the
* project defaults
*/
@Parameter
private Map<String, String> cleanUpProfile;

@Override
protected String[] getRequireBundles() {
return new String[] { "org.eclipse.jdt.ui" };
}

@Override
protected String getName() {
return "Perform Cleanup";
}

@Override
protected CleanUp createExecutable() {
return new CleanUp(project.getBasedir().toPath(), debug, cleanUpProfile);
}

@Override
protected void handleResult(CleanupResult result)
throws MojoFailureException {
Log log = getLog();
result.cleanups().forEach(cleanup -> {
log.info("Performed cleanup " + cleanup);
});
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*******************************************************************************
* Copyright (c) 2025 Christoph Läubrich and others.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Christoph Läubrich - initial API and implementation
*******************************************************************************/
package org.eclipse.tycho.cleancode;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import org.eclipse.tycho.eclipsebuild.EclipseBuildResult;

public class CleanupResult extends EclipseBuildResult {

private List<String> cleanups = new ArrayList<>();

public void addCleanup(String cleanup) {
cleanups.add(cleanup);
}

public Stream<String> cleanups() {
return this.cleanups.stream();
}

}
Loading

0 comments on commit 60d33e0

Please sign in to comment.