Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions plugin-maven/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
- `<scalafmt>` now reads the version from the `version` field in the scalafmt config file when no `<version>` is explicitly set, falling back to the built-in default only if neither is available. ([#2922](https://github.com/diffplug/spotless/pull/2922))
- Add `<toml>` format type with `<versionCatalog>` step for formatting and sorting Gradle version catalog files. ([#2916](https://github.com/diffplug/spotless/issues/2916))
- Add `<javaparserVersion>` option to `<cleanthat>`, allowing users to override the JavaParser version pulled in transitively by Cleanthat. ([#2903](https://github.com/diffplug/spotless/pull/2903))
- Add a `expandWildcardImports` API for java ([#2829](https://github.com/diffplug/spotless/pull/2930))
### Fixed
- Preserve case of JDBI named bind params that collide with SQL keywords (e.g. `:limit`, `:offset`) in the DBeaver SQL formatter. ([#2899](https://github.com/diffplug/spotless/pull/2899))
- The `-Dspotless.ratchetFrom=...` user property now takes priority over `<ratchetFrom>` configured in the plugin or in individual formatters, instead of being overridden by them. ([#2896](https://github.com/diffplug/spotless/pull/2896), fixes [#2842](https://github.com/diffplug/spotless/issues/2842))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand Down Expand Up @@ -412,7 +413,36 @@ private FormatterConfig getFormatterConfig() {
final Optional<String> userRatchetFrom = Optional.ofNullable((String) mavenSession.getUserProperties().get("ratchetFrom"));
final Optional<String> optionalRatchetFrom = Optional.ofNullable(this.ratchetFrom)
.filter(ratchet -> !RATCHETFROM_NONE.equals(ratchet));
return new FormatterConfig(baseDir, encoding, lineEndings, userRatchetFrom, optionalRatchetFrom, provisioner, p2Provisioner, fileLocator, formatterStepFactories, Optional.ofNullable(setLicenseHeaderYearsFromGitHistory), lintSuppressions);
Optional<Set<File>> projectClasspath = computeTypeSolverClasspath(resolver);
return new FormatterConfig(baseDir, encoding, lineEndings, userRatchetFrom, optionalRatchetFrom, provisioner, p2Provisioner, fileLocator, formatterStepFactories, Optional.ofNullable(setLicenseHeaderYearsFromGitHistory), lintSuppressions, projectClasspath);
}

private Optional<Set<File>> computeTypeSolverClasspath(ArtifactResolver resolver) {
Set<File> classpath = new LinkedHashSet<>();

// Add source roots (directories containing .java files for JavaParserTypeSolver)
for (String sourceRoot : project.getCompileSourceRoots()) {
File dir = new File(sourceRoot);
if (dir.exists()) {
classpath.add(dir);
}
}
for (String sourceRoot : project.getTestCompileSourceRoots()) {
File dir = new File(sourceRoot);
if (dir.exists()) {
classpath.add(dir);
}
}

// Resolve dependency JARs via RepositorySystem (for JarTypeSolver)
try {
Set<File> dependencyJars = resolver.resolveProjectDependencies(project, repositories);
classpath.addAll(dependencyJars);
} catch (Exception e) {
getLog().warn("Could not resolve project dependencies for expandWildcardImports: " + e.getMessage());
}

return Optional.of(classpath);
}

private FileLocator getFileLocator() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 DiffPlug
* Copyright 2016-2026 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -21,11 +21,13 @@
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
Expand Down Expand Up @@ -89,6 +91,34 @@ private DependencyResult resolveDependencies(DependencyRequest dependencyRequest
}
}

/**
* Resolves all dependencies (all scopes) of the given Maven project using the
* specified artifact repositories. Returns the set of resolved JAR files.
*/
public Set<File> resolveProjectDependencies(MavenProject project, List<RemoteRepository> artifactRepositories) {
List<Dependency> dependencies = project.getDependencies().stream()
.map(d -> new Dependency(
new DefaultArtifact(d.getGroupId(), d.getArtifactId(), d.getClassifier(), d.getType(), d.getVersion()),
d.getScope()))
.collect(toList());

if (dependencies.isEmpty()) {
return Collections.emptySet();
}

CollectRequest collectRequest = new CollectRequest(dependencies, null, artifactRepositories);
DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, null);
DependencyResult dependencyResult = resolveDependencies(dependencyRequest);

return dependencyResult.getArtifactResults()
.stream()
.map(ArtifactResult::getArtifact)
.filter(Objects::nonNull)
.map(Artifact::getFile)
.filter(Objects::nonNull)
.collect(toSet());
}

private void logResolved(ArtifactResult artifactResult) {
if (log.isDebugEnabled()) {
log.debug("Resolved artifact: " + artifactResult);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.io.File;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import com.diffplug.spotless.LineEnding;
import com.diffplug.spotless.LintSuppression;
Expand All @@ -38,9 +39,10 @@ public class FormatterConfig {
private final List<FormatterStepFactory> globalStepFactories;
private final Optional<String> spotlessSetLicenseHeaderYearsFromGitHistory;
private final List<LintSuppression> lintSuppressions;
private final Optional<Set<File>> projectClasspath;

public FormatterConfig(File baseDir, String encoding, LineEnding lineEndings, Optional<String> userRatchetFrom, Optional<String> ratchetFrom, Provisioner provisioner,
P2Provisioner p2Provisioner, FileLocator fileLocator, List<FormatterStepFactory> globalStepFactories, Optional<String> spotlessSetLicenseHeaderYearsFromGitHistory, List<LintSuppression> lintSuppressions) {
P2Provisioner p2Provisioner, FileLocator fileLocator, List<FormatterStepFactory> globalStepFactories, Optional<String> spotlessSetLicenseHeaderYearsFromGitHistory, List<LintSuppression> lintSuppressions, Optional<Set<File>> projectClasspath) {
this.encoding = encoding;
this.lineEndings = lineEndings;
this.userRatchetFrom = userRatchetFrom;
Expand All @@ -51,6 +53,7 @@ public FormatterConfig(File baseDir, String encoding, LineEnding lineEndings, Op
this.globalStepFactories = globalStepFactories;
this.spotlessSetLicenseHeaderYearsFromGitHistory = spotlessSetLicenseHeaderYearsFromGitHistory;
this.lintSuppressions = lintSuppressions;
this.projectClasspath = projectClasspath;
}

public String getEncoding() {
Expand Down Expand Up @@ -92,4 +95,8 @@ public FileLocator getFileLocator() {
public List<LintSuppression> getLintSuppressions() {
return unmodifiableList(lintSuppressions);
}

public Optional<Set<File>> getProjectClasspath() {
return projectClasspath;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ private Optional<String> optionalRatchetFrom() {
}

private FormatterStepConfig stepConfig(Charset encoding, FormatterConfig config) {
return new FormatterStepConfig(encoding, licenseHeaderDelimiter(), ratchetFrom(config), config.getProvisioner(), config.getP2Provisioner(), config.getFileLocator(), config.getSpotlessSetLicenseHeaderYearsFromGitHistory());
return new FormatterStepConfig(encoding, licenseHeaderDelimiter(), ratchetFrom(config), config.getProvisioner(), config.getP2Provisioner(), config.getFileLocator(), config.getSpotlessSetLicenseHeaderYearsFromGitHistory(), config.getProjectClasspath());
}

private static List<FormatterStepFactory> gatherStepFactories(List<FormatterStepFactory> allGlobal, List<FormatterStepFactory> allConfigured) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
*/
package com.diffplug.spotless.maven;

import java.io.File;
import java.nio.charset.Charset;
import java.util.Optional;
import java.util.Set;

import com.diffplug.spotless.Provisioner;
import com.diffplug.spotless.extra.P2Provisioner;
Expand All @@ -30,15 +32,17 @@ public class FormatterStepConfig {
private final P2Provisioner p2Provisioner;
private final FileLocator fileLocator;
private final Optional<String> spotlessSetLicenseHeaderYearsFromGitHistory;
private final Optional<Set<File>> projectClasspath;

public FormatterStepConfig(Charset encoding, String licenseHeaderDelimiter, Optional<String> ratchetFrom, Provisioner provisioner, P2Provisioner p2Provisioner, FileLocator fileLocator, Optional<String> spotlessSetLicenseHeaderYearsFromGitHistory) {
public FormatterStepConfig(Charset encoding, String licenseHeaderDelimiter, Optional<String> ratchetFrom, Provisioner provisioner, P2Provisioner p2Provisioner, FileLocator fileLocator, Optional<String> spotlessSetLicenseHeaderYearsFromGitHistory, Optional<Set<File>> projectClasspath) {
this.encoding = encoding;
this.licenseHeaderDelimiter = licenseHeaderDelimiter;
this.ratchetFrom = ratchetFrom;
this.provisioner = provisioner;
this.p2Provisioner = p2Provisioner;
this.fileLocator = fileLocator;
this.spotlessSetLicenseHeaderYearsFromGitHistory = spotlessSetLicenseHeaderYearsFromGitHistory;
this.projectClasspath = projectClasspath;
}

public Charset getEncoding() {
Expand Down Expand Up @@ -68,4 +72,8 @@ public FileLocator getFileLocator() {
public Optional<String> spotlessSetLicenseHeaderYearsFromGitHistory() {
return spotlessSetLicenseHeaderYearsFromGitHistory;
}

public Optional<Set<File>> getProjectClasspath() {
return projectClasspath;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2025-2026 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.maven.java;

import java.io.File;
import java.util.Collections;
import java.util.Set;

import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.java.ExpandWildcardImportsStep;
import com.diffplug.spotless.maven.FormatterStepConfig;
import com.diffplug.spotless.maven.FormatterStepFactory;

public class ExpandWildcardImports implements FormatterStepFactory {
@Override
public FormatterStep newFormatterStep(FormatterStepConfig config) {
Set<File> classpath = config.getProjectClasspath().orElse(Collections.emptySet());
return ExpandWildcardImportsStep.create(classpath, config.getProvisioner());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ public void addForbidWildcardImports(ForbidWildcardImports forbidWildcardImports
addStepFactory(forbidWildcardImports);
}

public void addExpandWildcardImports(ExpandWildcardImports expandWildcardImports) {
addStepFactory(expandWildcardImports);
}

public void addForbidModuleImports(ForbidModuleImports forbidModuleImports) {
addStepFactory(forbidModuleImports);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2025-2026 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.maven.java;

import org.junit.jupiter.api.Test;

import com.diffplug.spotless.maven.MavenIntegrationHarness;

class ExpandWildcardImportsStepTest extends MavenIntegrationHarness {

@Test
void expandWildcardImports() throws Exception {
writePomWithJavaSteps("<expandWildcardImports/>");

// Create supporting classes in source roots so JavaParserTypeSolver can resolve them
setFile("src/main/java/foo/bar/AnotherClassInSamePackage.java")
.toResource("java/expandwildcardimports/AnotherClassInSamePackage.test");
setFile("src/main/java/foo/bar/baz/AnotherImportedClass.java")
.toResource("java/expandwildcardimports/AnotherImportedClass.test");
// Source for the annotation used in the test (resolves via source root, not JAR)
setFile("src/main/java/org/example/SomeAnnotation.java")
.toContent("package org.example;\n\npublic @interface SomeAnnotation {}\n");

String path = "src/main/java/foo/bar/JavaClassWithWildcards.java";
setFile(path).toResource("java/expandwildcardimports/JavaClassWithWildcardsUnformatted.test");

mavenRunner().withArguments("spotless:apply").runNoError();

assertFile(path).sameAsResource("java/expandwildcardimports/JavaClassWithWildcardsFormatted.test");
}
}
Loading