Skip to content

Commit d8b09c8

Browse files
SONARJAVA-5845 Add new ModuleMetadata public API
1 parent ed71c8c commit d8b09c8

File tree

8 files changed

+221
-24
lines changed

8 files changed

+221
-24
lines changed

java-checks-test-sources/default/src/test/java/checks/tests/SpringBootEmptyMethodsCheckSample.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
class SpringBootSanityTest {
88
// Compliant, first time we encounter a spring sanity test
99
@Test
10-
void contextLoads() {
11-
}
10+
void contextLoads() {}
1211

1312
// Noncompliant@+2
1413
@Test
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* SonarQube Java
3+
* Copyright (C) 2012-2025 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
package org.sonar.java;
18+
19+
import org.sonar.api.config.Configuration;
20+
import org.sonar.java.model.JavaVersionImpl;
21+
import org.sonar.plugins.java.api.JavaVersion;
22+
import org.sonar.plugins.java.api.internal.ModuleMetadata;
23+
24+
public class DefaultModuleMetadata implements ModuleMetadata {
25+
26+
private final JavaVersion javaVersion;
27+
private final String moduleKey;
28+
private final boolean ignoreUnnamedModuleForSplitPackage;
29+
30+
public DefaultModuleMetadata(SonarComponents sonarComponents, Configuration configuration) {
31+
this.javaVersion = JavaVersionImpl.readFromConfiguration(configuration);
32+
this.moduleKey = sonarComponents.getModuleKey();
33+
this.ignoreUnnamedModuleForSplitPackage = sonarComponents.shouldIgnoreUnnamedModuleForSplitPackage();
34+
}
35+
36+
@Override
37+
public JavaVersion javaVersion() {
38+
return javaVersion;
39+
}
40+
41+
@Override
42+
public String moduleKey() {
43+
return moduleKey;
44+
}
45+
46+
@Override
47+
public boolean shouldIgnoreUnnamedModuleForSplitPackage() {
48+
return ignoreUnnamedModuleForSplitPackage;
49+
}
50+
51+
}

java-frontend/src/main/java/org/sonar/java/model/JavaVersionImpl.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
package org.sonar.java.model;
1818

1919
import java.util.Locale;
20+
import java.util.Optional;
2021
import org.slf4j.Logger;
2122
import org.slf4j.LoggerFactory;
23+
import org.sonar.api.config.Configuration;
2224
import org.sonar.plugins.java.api.JavaVersion;
2325

2426
public class JavaVersionImpl implements JavaVersion {
@@ -230,4 +232,22 @@ private static int convertJavaVersionString(String javaVersion) {
230232
return Integer.parseInt(cleanedVersion);
231233
}
232234

235+
public static JavaVersion readFromConfiguration(Configuration config) {
236+
Optional<String> javaVersionAsString = config.get(SOURCE_VERSION);
237+
if (!javaVersionAsString.isPresent()) {
238+
return new JavaVersionImpl();
239+
}
240+
String enablePreviewAsString = config.get(ENABLE_PREVIEW).orElse("false");
241+
242+
JavaVersion javaVersion = fromString(javaVersionAsString.get(), enablePreviewAsString);
243+
if (javaVersion.arePreviewFeaturesEnabled() && javaVersion.asInt() < MAX_SUPPORTED) {
244+
LOG.warn("sonar.java.enablePreview is set but will be discarded as the Java version is less than the max" +
245+
" supported version ({} < {})", javaVersion.asInt(), MAX_SUPPORTED);
246+
javaVersion = new JavaVersionImpl(javaVersion.asInt(), false);
247+
}
248+
LOG.info("Configured Java source version ({}): {}, preview features enabled ({}): {}",
249+
SOURCE_VERSION, javaVersion.asInt(), ENABLE_PREVIEW, javaVersion.arePreviewFeaturesEnabled());
250+
return javaVersion;
251+
}
252+
233253
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* SonarQube Java
3+
* Copyright (C) 2012-2025 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
package org.sonar.plugins.java.api.internal;
18+
19+
import org.sonar.api.batch.ScannerSide;
20+
import org.sonar.java.annotations.Beta;
21+
import org.sonar.plugins.java.api.JavaVersion;
22+
23+
24+
/**
25+
* Interface to access metadata about the module being analyzed by a Sensor.
26+
* For internal use only, this API will not be supported for custom plugins.
27+
*/
28+
@Beta
29+
@ScannerSide
30+
public interface ModuleMetadata {
31+
32+
/**
33+
* Returns the Java version of the module being analyzed.
34+
*/
35+
JavaVersion javaVersion();
36+
37+
/**
38+
* Returns the module key of the module being analyzed.
39+
*/
40+
String moduleKey();
41+
42+
/**
43+
* Describes whether input files should be parsed while ignoring unnamed split modules.
44+
*/
45+
boolean shouldIgnoreUnnamedModuleForSplitPackage();
46+
47+
}
48+
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* SonarQube Java
3+
* Copyright (C) 2012-2025 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
package org.sonar.java;
18+
19+
import java.io.File;
20+
import java.util.Optional;
21+
import org.junit.jupiter.api.Test;
22+
import org.sonar.api.batch.bootstrap.ProjectDefinition;
23+
import org.sonar.api.batch.sensor.SensorContext;
24+
import org.sonar.api.config.Configuration;
25+
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
import static org.mockito.Mockito.doReturn;
28+
import static org.mockito.Mockito.mock;
29+
30+
class DefaultModuleMetadataTest {
31+
32+
@Test
33+
void test() {
34+
var sonarComponents = mockSonarComponents();
35+
var config = mockConfiguration();
36+
sonarComponents.setSensorContext(mockSensorContext(config));
37+
var defaultModuleMetadata = new DefaultModuleMetadata(sonarComponents, config);
38+
39+
assertThat(defaultModuleMetadata.moduleKey()).isEqualTo("pmodule/cmodule");
40+
assertThat(defaultModuleMetadata.javaVersion().asInt()).isEqualTo(-1);
41+
assertThat(defaultModuleMetadata.shouldIgnoreUnnamedModuleForSplitPackage()).isFalse();
42+
}
43+
44+
@Test
45+
void testWithJavaVersion() {
46+
var sonarComponents = mockSonarComponents();
47+
var config = mockConfiguration("sonar.java.source", "11");
48+
sonarComponents.setSensorContext(mockSensorContext(config));
49+
var defaultModuleMetadata = new DefaultModuleMetadata(sonarComponents, config);
50+
51+
assertThat(defaultModuleMetadata.moduleKey()).isEqualTo("pmodule/cmodule");
52+
assertThat(defaultModuleMetadata.javaVersion().asInt()).isEqualTo(11);
53+
}
54+
55+
@Test
56+
void testWithShouldIgnoreUnnamed() {
57+
var sonarComponents = mockSonarComponents();
58+
var config = mockConfiguration("sonar.java.ignoreUnnamedModuleForSplitPackage", "true");
59+
sonarComponents.setSensorContext(mockSensorContext(config));
60+
var defaultModuleMetadata = new DefaultModuleMetadata(sonarComponents, config);
61+
62+
assertThat(defaultModuleMetadata.moduleKey()).isEqualTo("pmodule/cmodule");
63+
assertThat(defaultModuleMetadata.shouldIgnoreUnnamedModuleForSplitPackage()).isTrue();
64+
}
65+
66+
private SonarComponents mockSonarComponents() {
67+
var rootProj = mock(ProjectDefinition.class);
68+
doReturn(new File("/foo/bar/proj")).when(rootProj).getBaseDir();
69+
var childModule = mock(ProjectDefinition.class);
70+
doReturn(new File("/foo/bar/proj/pmodule/cmodule")).when(childModule).getBaseDir();
71+
doReturn(rootProj).when(childModule).getParent();
72+
73+
return new SonarComponents(null, null, null, null, null, null, childModule);
74+
}
75+
76+
private Configuration mockConfiguration(String... keysAndValues) {
77+
Configuration configuration = mock(Configuration.class);
78+
for (int i = 0; i < keysAndValues.length; i++) {
79+
String key = keysAndValues[i++];
80+
String value = keysAndValues[i];
81+
doReturn(Optional.of(value)).when(configuration).get(key);
82+
if(value.equals("true") || value.equals("false")) {
83+
doReturn(Optional.of(Boolean.valueOf(value))).when(configuration).getBoolean(key);
84+
}
85+
}
86+
return configuration;
87+
}
88+
89+
private SensorContext mockSensorContext(Configuration config) {
90+
var sctx = mock(SensorContext.class);
91+
doReturn(config).when(sctx).config();
92+
return sctx;
93+
}
94+
95+
}

sonar-java-plugin/src/main/java/org/sonar/plugins/java/JavaPlugin.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.sonar.api.config.PropertyDefinition;
2929
import org.sonar.java.AnalysisWarningsWrapper;
3030
import org.sonar.java.DefaultJavaResourceLocator;
31+
import org.sonar.java.DefaultModuleMetadata;
3132
import org.sonar.java.JavaConstants;
3233
import org.sonar.java.SonarComponents;
3334
import org.sonar.java.classpath.ClasspathForMain;
@@ -117,6 +118,7 @@ public void define(Context context) {
117118
PostAnalysisIssueFilter.class));
118119

119120
list.add(AnalysisWarningsWrapper.class);
121+
list.add(DefaultModuleMetadata.class);
120122
context.addExtensions(Collections.unmodifiableList(list));
121123
}
122124

sonar-java-plugin/src/main/java/org/sonar/plugins/java/JavaSensor.java

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public void execute(SensorContext context) {
111111

112112
Measurer measurer = new Measurer(context, noSonarFilter);
113113

114-
JavaVersion javaVersion = getJavaVersion();
114+
JavaVersion javaVersion = JavaVersionImpl.readFromConfiguration(settings);
115115
telemetry.aggregateAsSortedSet(JAVA_LANGUAGE_VERSION, javaVersion.toString());
116116
telemetry.aggregateAsCounter(JAVA_MODULE_COUNT, 1L);
117117

@@ -181,24 +181,6 @@ private Iterable<InputFile> javaFiles(InputFile.Type type) {
181181
return fs.inputFiles(fs.predicates().and(fs.predicates().hasLanguage(Java.KEY), fs.predicates().hasType(type)));
182182
}
183183

184-
private JavaVersion getJavaVersion() {
185-
Optional<String> javaVersionAsString = settings.get(JavaVersion.SOURCE_VERSION);
186-
if (!javaVersionAsString.isPresent()) {
187-
return new JavaVersionImpl();
188-
}
189-
String enablePreviewAsString = settings.get(JavaVersion.ENABLE_PREVIEW).orElse("false");
190-
191-
JavaVersion javaVersion = JavaVersionImpl.fromString(javaVersionAsString.get(), enablePreviewAsString);
192-
if (javaVersion.arePreviewFeaturesEnabled() && javaVersion.asInt() < JavaVersionImpl.MAX_SUPPORTED) {
193-
LOG.warn("sonar.java.enablePreview is set but will be discarded as the Java version is less than the max" +
194-
" supported version ({} < {})", javaVersion.asInt(), JavaVersionImpl.MAX_SUPPORTED);
195-
javaVersion = new JavaVersionImpl(javaVersion.asInt(), false);
196-
}
197-
LOG.info("Configured Java source version ({}): {}, preview features enabled ({}): {}",
198-
JavaVersion.SOURCE_VERSION, javaVersion.asInt(), JavaVersion.ENABLE_PREVIEW, javaVersion.arePreviewFeaturesEnabled());
199-
return javaVersion;
200-
}
201-
202184
@Override
203185
public String toString() {
204186
return getClass().getSimpleName();

sonar-java-plugin/src/test/java/org/sonar/plugins/java/JavaPluginTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ void sonarLint_9_9_extensions() {
4040
Plugin.Context context = new Plugin.Context(runtime);
4141
javaPlugin.define(context);
4242
assertThat(context.getExtensions())
43-
.hasSize(19)
43+
.hasSize(20)
4444
.contains(SonarLintCache.class);
4545
}
4646

@@ -51,7 +51,7 @@ void sonarqube_9_9_extensions() {
5151
Plugin.Context context = new Plugin.Context(sqCommunity);
5252
javaPlugin.define(context);
5353
assertThat(context.getExtensions())
54-
.hasSize(35)
54+
.hasSize(36)
5555
.doesNotContain(Jasper.class);
5656
}
5757

@@ -61,7 +61,7 @@ void sonarqube_9_9_commercial_extensions() {
6161
Plugin.Context context = new Plugin.Context(sqEnterprise);
6262
javaPlugin.define(context);
6363
assertThat(context.getExtensions())
64-
.hasSize(36)
64+
.hasSize(37)
6565
.contains(Jasper.class);
6666
}
6767

0 commit comments

Comments
 (0)