Skip to content

Commit d048dd6

Browse files
committed
Rough draft of configuring the plugin based on the Maven plugin configuration
1 parent 8c610f8 commit d048dd6

File tree

4 files changed

+241
-0
lines changed

4 files changed

+241
-0
lines changed

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ dependencies {
8686
intellijIdeaCommunity("2024.1.7")
8787

8888
bundledPlugin("com.intellij.java")
89+
bundledPlugin("org.jetbrains.idea.maven")
8990

9091
testFramework(TestFrameworkType.Platform)
9192
}
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
package org.infernus.idea.checkstyle.maven;
2+
3+
import com.intellij.openapi.project.Project;
4+
import java.io.File;
5+
import java.net.URI;
6+
import java.net.URISyntaxException;
7+
import java.nio.file.Files;
8+
import java.nio.file.InvalidPathException;
9+
import java.nio.file.Path;
10+
import java.util.Spliterator;
11+
import java.util.Spliterators;
12+
import java.util.TreeSet;
13+
import java.util.stream.StreamSupport;
14+
import org.infernus.idea.checkstyle.CheckstyleProjectService;
15+
import org.infernus.idea.checkstyle.config.PluginConfigurationBuilder;
16+
import org.infernus.idea.checkstyle.config.PluginConfigurationManager;
17+
import org.infernus.idea.checkstyle.model.ConfigurationLocation;
18+
import org.infernus.idea.checkstyle.model.ConfigurationLocationFactory;
19+
import org.infernus.idea.checkstyle.model.ConfigurationType;
20+
import org.infernus.idea.checkstyle.model.NamedScopeHelper;
21+
import org.jetbrains.annotations.NotNull;
22+
import org.jetbrains.idea.maven.importing.MavenAfterImportConfigurator;
23+
import org.jetbrains.idea.maven.model.MavenId;
24+
import org.jetbrains.idea.maven.project.MavenProject;
25+
26+
@SuppressWarnings("UnstableApiUsage")
27+
public class MavenCheckstyleConfigurator implements MavenAfterImportConfigurator {
28+
29+
private static final MavenId CHECKSTYLE_MAVEN_ID =
30+
new MavenId("com.puppycrawl.tools", "checkstyle", null);
31+
private static final MavenId MAVEN_CHECKSTYLE_PLUGIN_MAVEN_ID =
32+
new MavenId("org.apache.maven.plugins", "maven-checkstyle-plugin", null);
33+
private static final String MAVEN_CONFIG_LOCATION_ID = "maven-config-location";
34+
35+
@Override
36+
public void afterImport(@NotNull final MavenAfterImportConfigurator.Context context) {
37+
final var project = context.getProject();
38+
39+
// TODO: Current CheckStyle IntelliJ plugin supports specifying the active rule for a given
40+
// Module. The code here can probably support that as well, if two modules use different
41+
// configurations.
42+
43+
// TODO: The first MavenProject found with a Maven Checkstyle Plugin will modify the settings
44+
// for the Project. Might be useful to have a more deterministic way for this to work.
45+
final var mavenProjectWithModules =
46+
StreamSupport.stream(
47+
Spliterators.spliteratorUnknownSize(
48+
context.getMavenProjectsWithModules().iterator(), Spliterator.ORDERED),
49+
false)
50+
.filter(
51+
mavenProjectWithModulesToFilter -> {
52+
final var mavenProject = mavenProjectWithModulesToFilter.getMavenProject();
53+
final var checkstyleMavenPlugin =
54+
mavenProject.findPlugin(
55+
MAVEN_CHECKSTYLE_PLUGIN_MAVEN_ID.getGroupId(),
56+
MAVEN_CHECKSTYLE_PLUGIN_MAVEN_ID.getArtifactId());
57+
58+
return checkstyleMavenPlugin != null;
59+
})
60+
.findFirst()
61+
.orElse(null);
62+
63+
if (mavenProjectWithModules == null) {
64+
return;
65+
}
66+
67+
final var mavenProject = mavenProjectWithModules.getMavenProject();
68+
final var checkstyleMavenPlugin =
69+
mavenProject.findPlugin(
70+
MAVEN_CHECKSTYLE_PLUGIN_MAVEN_ID.getGroupId(),
71+
MAVEN_CHECKSTYLE_PLUGIN_MAVEN_ID.getArtifactId());
72+
73+
if (checkstyleMavenPlugin == null) {
74+
return;
75+
}
76+
77+
final var checkstyleDependencyMavenId =
78+
checkstyleMavenPlugin.getDependencies().stream()
79+
.filter(
80+
dependency ->
81+
CHECKSTYLE_MAVEN_ID.equals(dependency.getGroupId(), dependency.getArtifactId()))
82+
.findFirst()
83+
.orElse(null);
84+
85+
final var pluginConfigurationManager = project.getService(PluginConfigurationManager.class);
86+
final var currentPluginConfiguration = pluginConfigurationManager.getCurrent();
87+
final var pluginConfigurationBuilder =
88+
PluginConfigurationBuilder.from(currentPluginConfiguration);
89+
// TODO: This will be null if checkstyle isn't declared explicitly.
90+
// Meaning the transitive dependency version won't be found.
91+
// Would be great to resolve that transitive version somehow.
92+
if (checkstyleDependencyMavenId != null && checkstyleDependencyMavenId.getVersion() != null) {
93+
// Checkstyle Version
94+
pluginConfigurationBuilder.withCheckstyleVersion(checkstyleDependencyMavenId.getVersion());
95+
}
96+
97+
// Checkstyle Third Party Rules
98+
// TODO: This doesn't differentiate between an additional dependency that may or may
99+
// not be providing rules. Not sure if that matters or how that would be worked around.
100+
final var thirdPartyClassPaths =
101+
checkstyleMavenPlugin.getDependencies().stream()
102+
.filter(
103+
dependency -> {
104+
if (!CHECKSTYLE_MAVEN_ID.equals(
105+
dependency.getGroupId(), dependency.getArtifactId())) {
106+
return false;
107+
}
108+
109+
if (dependency.getArtifactId() == null
110+
|| dependency.getGroupId() == null
111+
|| dependency.getVersion() == null) {
112+
return false;
113+
}
114+
115+
return true;
116+
})
117+
.map(
118+
dependency -> {
119+
final var dependencyRelativePath =
120+
Path.of(
121+
dependency.getGroupId().replace(".", File.separator),
122+
dependency.getArtifactId(),
123+
dependency.getVersion(),
124+
// TODO: This doesn't support classifiers.
125+
dependency.getArtifactId() + "-" + dependency.getVersion() + ".jar");
126+
final var dependencyPath =
127+
mavenProject.getLocalRepository().toPath().resolve(dependencyRelativePath);
128+
129+
return dependencyPath.toAbsolutePath().toString();
130+
})
131+
.toList();
132+
pluginConfigurationBuilder.withThirdPartyClassPath(thirdPartyClassPaths);
133+
134+
final var checkstyleMavenPluginConfiguration = checkstyleMavenPlugin.getConfigurationElement();
135+
if (checkstyleMavenPluginConfiguration != null) {
136+
// Checkstyle Config File
137+
final var configLocationElement =
138+
checkstyleMavenPluginConfiguration.getChild("configLocation");
139+
if (configLocationElement != null && configLocationElement.getText() != null) {
140+
final String mavenPluginConfigLocation = configLocationElement.getText();
141+
// This must come after the PluginConfigurationBuilder is modified with the new
142+
// CheckStyle version and the new third party classpaths.
143+
final var tempConfiguration = pluginConfigurationBuilder.build();
144+
final var checkstyleProjectService =
145+
CheckstyleProjectService.forVersion(
146+
project,
147+
tempConfiguration.getCheckstyleVersion(),
148+
tempConfiguration.getThirdPartyClasspath());
149+
final var configurationLocation =
150+
createConfigurationLocation(
151+
project, mavenProject, checkstyleProjectService, mavenPluginConfigLocation);
152+
153+
final var configLocations = new TreeSet<>(currentPluginConfiguration.getLocations());
154+
configLocations.removeIf(location -> MAVEN_CONFIG_LOCATION_ID.equals(location.getId()));
155+
configLocations.add(configurationLocation);
156+
pluginConfigurationBuilder.withLocations(configLocations);
157+
158+
final var activeConfigLocationIds =
159+
new TreeSet<>(currentPluginConfiguration.getActiveLocationIds());
160+
activeConfigLocationIds.removeIf(MAVEN_CONFIG_LOCATION_ID::equals);
161+
activeConfigLocationIds.add(configurationLocation.getId());
162+
pluginConfigurationBuilder.withActiveLocationIds(activeConfigLocationIds);
163+
}
164+
}
165+
166+
// TODO:
167+
// Checkstyle Scan Scope
168+
169+
final var newPluginConfiguration = pluginConfigurationBuilder.build();
170+
if (!currentPluginConfiguration.equals(newPluginConfiguration)) {
171+
pluginConfigurationManager.setCurrent(pluginConfigurationBuilder.build(), true);
172+
}
173+
}
174+
175+
private ConfigurationLocation createConfigurationLocation(
176+
final Project project,
177+
final MavenProject mavenProject,
178+
final CheckstyleProjectService checkstyleProjectService,
179+
final String mavenPluginConfigLocation) {
180+
final var configurationLocationFactory = project.getService(ConfigurationLocationFactory.class);
181+
182+
ConfigurationType configurationType = null;
183+
String configLocation = mavenPluginConfigLocation;
184+
185+
try {
186+
final var mavenPluginConfigLocationPath = Path.of(mavenPluginConfigLocation);
187+
if (mavenPluginConfigLocationPath.isAbsolute()
188+
&& Files.isReadable(mavenPluginConfigLocationPath)) {
189+
configurationType = ConfigurationType.LOCAL_FILE;
190+
} else if (Files.isReadable(
191+
Path.of(mavenProject.getDirectory()).resolve(mavenPluginConfigLocationPath))) {
192+
configurationType = ConfigurationType.PROJECT_RELATIVE;
193+
// TODO: Seems a bit odd that this needs to be set to an absolute path, instead of a
194+
// relative path.
195+
configLocation =
196+
Path.of(mavenProject.getDirectory()).resolve(mavenPluginConfigLocationPath).toString();
197+
}
198+
} catch (final InvalidPathException ignored) {
199+
}
200+
201+
if (configurationType == null) {
202+
try {
203+
// This can also be a file:// URI. This still resolves fine with the HTTP_URL
204+
// implementation, so just leaving it alone. This is probably a bit confusing
205+
// and has potential to break in the future.
206+
new URI(mavenPluginConfigLocation);
207+
configurationType = ConfigurationType.HTTP_URL;
208+
} catch (final URISyntaxException ignored) {
209+
}
210+
}
211+
212+
final var classLoader = checkstyleProjectService.underlyingClassLoader();
213+
final var resource = classLoader.getResource(mavenPluginConfigLocation);
214+
if (resource != null) {
215+
configurationType = ConfigurationType.PLUGIN_CLASSPATH;
216+
}
217+
218+
if (configurationType == null) {
219+
throw new RuntimeException(
220+
"Unable to identify ConfigurationType for configured location: "
221+
+ mavenPluginConfigLocation);
222+
}
223+
224+
return configurationLocationFactory.create(
225+
project,
226+
MAVEN_CONFIG_LOCATION_ID,
227+
configurationType,
228+
configLocation,
229+
"Maven Config Location",
230+
// TODO: What should this be?
231+
NamedScopeHelper.getDefaultScope(project));
232+
}
233+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<idea-plugin>
3+
<extensions defaultExtensionNs="org.jetbrains.idea.maven">
4+
<importing.afterImportConfigurator implementation="org.infernus.idea.checkstyle.maven.MavenCheckstyleConfigurator" />
5+
</extensions>
6+
</idea-plugin>

src/main/resources/META-INF/plugin.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<depends>com.intellij.modules.vcs</depends>
2222

2323
<depends>com.intellij.modules.java</depends>
24+
<depends optional="true" config-file="checkstyle-idea-maven.xml">org.jetbrains.idea.maven</depends>
2425

2526
<change-notes>
2627
<![CDATA[

0 commit comments

Comments
 (0)