Skip to content

Commit 577d413

Browse files
committed
Issue #78: extract: grab property info
1 parent f92362c commit 577d413

File tree

8 files changed

+243
-6
lines changed

8 files changed

+243
-6
lines changed

config/findbugs-exclude.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,9 @@
2727
<Class name="~.*\.Immutable.*"/>
2828
<Bug pattern="NP_METHOD_PARAMETER_TIGHTENS_ANNOTATION"/>
2929
</Match>
30+
<Match>
31+
<!-- We can't change generated source code. -->
32+
<Class name="~.*\.GsonAdapters.*"/>
33+
<Bug pattern="URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD"/>
34+
</Match>
3035
</FindBugsFilter>

config/sevntu_suppressions.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
55

66
<suppressions>
7-
<suppress checks="MultipleStringLiteralsExtended" files=".*[\\/]src[\\/]test[\\/]"/>
7+
<suppress checks="MultipleStringLiteralsExtended" files="[\\/]ExtractInfoGeneratorTest.java$|.*[\\/]src[\\/]test[\\/]"/>
88
</suppressions>

config/suppressions.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<suppress checks="MagicNumber" files=".*[\\/]src[\\/]test[\\/]"/>
1414
<suppress checks="AvoidStaticImport" files=".*[\\/]src[\\/]test[\\/]"/>
1515
<suppress checks="WriteTag" files=".*[\\/]src[\\/]test[\\/]"/>
16-
<suppress checks="MultipleStringLiterals" files=".*[\\/]src[\\/]test[\\/]"/>
16+
<suppress checks="MultipleStringLiterals" files="[\\/]ExtractInfoGeneratorTest.java$|.*[\\/]src[\\/]test[\\/]"/>
1717

1818
<suppress checks="AbstractClassName" files=".*[\\/]data[\\/]"/>
1919
</suppressions>

src/main/java/com/github/checkstyle/regression/data/ModuleExtractInfo.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
package com.github.checkstyle.regression.data;
2121

22+
import java.util.List;
23+
2224
import org.immutables.gson.Gson;
2325
import org.immutables.value.Value;
2426

@@ -48,11 +50,46 @@ public abstract class ModuleExtractInfo {
4850
*/
4951
public abstract String parent();
5052

53+
/**
54+
* The properties of this module.
55+
* @return the properties of this module
56+
*/
57+
public abstract List<ModuleProperty> properties();
58+
5159
/**
5260
* The full qualified name of this module.
5361
* @return the full qualified name of this module
5462
*/
5563
public String fullName() {
5664
return packageName() + "." + name();
5765
}
66+
67+
/** Represents a property of checkstyle module. */
68+
@Gson.TypeAdapters
69+
@Value.Immutable
70+
public interface ModuleProperty {
71+
/**
72+
* The name of this property.
73+
* @return the name of this property
74+
*/
75+
String name();
76+
77+
/**
78+
* The type of this property.
79+
* The value should be one of the followings:
80+
* - Pattern
81+
* - SeverityLevel
82+
* - boolean
83+
* - Scope
84+
* - double[]
85+
* - int[]
86+
* - String[]
87+
* - String
88+
* - URI
89+
* - AccessModifier[]
90+
* - int
91+
* @return the type of this property
92+
*/
93+
String type();
94+
}
5895
}

src/main/resources/com/github/checkstyle/regression/extract/ExtractInfoGeneratorTest.java

Lines changed: 168 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,26 @@
1919

2020
package com.puppycrawl.tools.checkstyle;
2121

22+
import java.beans.PropertyDescriptor;
2223
import java.io.File;
23-
import java.io.IOException;
2424
import java.nio.charset.Charset;
2525
import java.nio.file.Files;
2626
import java.nio.file.StandardOpenOption;
2727
import java.util.ArrayList;
2828
import java.util.Arrays;
2929
import java.util.Comparator;
3030
import java.util.List;
31+
import java.util.Set;
32+
import java.util.TreeSet;
3133

34+
import org.apache.commons.beanutils.PropertyUtils;
3235
import org.junit.Test;
3336

37+
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
38+
import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
39+
import com.puppycrawl.tools.checkstyle.checks.javadoc.AbstractJavadocCheck;
3440
import com.puppycrawl.tools.checkstyle.internal.CheckUtil;
41+
import com.puppycrawl.tools.checkstyle.internal.TestUtils;
3542
import com.puppycrawl.tools.checkstyle.utils.ModuleReflectionUtils;
3643

3744
/**
@@ -40,12 +47,57 @@
4047
* @author LuoLiangchen
4148
*/
4249
public class ExtractInfoGeneratorTest {
50+
/** Modules which do not have global properties to drop. */
51+
private static final List<String> XML_FILESET_LIST = Arrays.asList(
52+
"TreeWalker",
53+
"Checker",
54+
"Header",
55+
"Translation",
56+
"SeverityMatchFilter",
57+
"SuppressionFilter",
58+
"SuppressWarningsFilter",
59+
"BeforeExecutionExclusionFileFilter",
60+
"RegexpHeader",
61+
"RegexpOnFilename",
62+
"RegexpSingleline",
63+
"RegexpMultiline",
64+
"JavadocPackage",
65+
"NewlineAtEndOfFile",
66+
"UniqueProperties",
67+
"FileLength",
68+
"FileTabCharacter"
69+
);
70+
71+
/** Properties of abstract check. */
72+
private static final Set<String> CHECK_PROPERTIES = getProperties(AbstractCheck.class);
73+
74+
/** Properties of abstract Javadoc check. */
75+
private static final Set<String> JAVADOC_CHECK_PROPERTIES =
76+
getProperties(AbstractJavadocCheck.class);
77+
78+
/** Properties of abstract file-set check. */
79+
private static final Set<String> FILESET_PROPERTIES = getProperties(AbstractFileSetCheck.class);
80+
81+
/** Properties without document. */
82+
private static final List<String> UNDOCUMENTED_PROPERTIES = Arrays.asList(
83+
"Checker.classLoader",
84+
"Checker.classloader",
85+
"Checker.moduleClassLoader",
86+
"Checker.moduleFactory",
87+
"TreeWalker.classLoader",
88+
"TreeWalker.moduleFactory",
89+
"TreeWalker.cacheFile",
90+
"TreeWalker.upChild",
91+
"SuppressWithNearbyCommentFilter.fileContents",
92+
"SuppressionCommentFilter.fileContents"
93+
);
94+
4395
/**
4496
* Generates the extract info file named as "checkstyle_modules.json".
45-
* @throws IOException failure when generating the file
97+
* @throws Exception failure when generating the file
4698
*/
4799
@Test
48-
public void generateExtractInfoFile() throws IOException {
100+
public void generateExtractInfoFile() throws Exception {
49101
final List<Class<?>> modules = new ArrayList<>(CheckUtil.getCheckstyleModules());
50102
modules.sort(Comparator.comparing(Class::getSimpleName));
51103
final JsonUtil.JsonArray moduleJsonArray = new JsonUtil.JsonArray();
@@ -62,8 +114,10 @@ public void generateExtractInfoFile() throws IOException {
62114
* Creates Json object for a module from the module class.
63115
* @param clazz the given module class
64116
* @return the Json object describing the extract info of the module
117+
* @throws Exception failure when creating Json object
65118
*/
66-
private static JsonUtil.JsonObject createJsonObjectFromModuleClass(Class<?> clazz) {
119+
private static JsonUtil.JsonObject createJsonObjectFromModuleClass(Class<?> clazz)
120+
throws Exception {
67121
final JsonUtil.JsonObject object = new JsonUtil.JsonObject();
68122

69123
final String name = clazz.getSimpleName();
@@ -94,6 +148,116 @@ else if (ModuleReflectionUtils.isRootModule(clazz)) {
94148
object.add("interfaces", interfaces);
95149
object.add("hierarchies", hierarchies);
96150

151+
final JsonUtil.JsonArray properties = new JsonUtil.JsonArray();
152+
for (String propertyName : getNecessaryProperties(clazz)) {
153+
final JsonUtil.JsonObject property = new JsonUtil.JsonObject();
154+
property.addProperty("name", propertyName);
155+
Arrays.stream(PropertyUtils.getPropertyDescriptors(clazz))
156+
.filter(p -> p.getName().equals(propertyName))
157+
.map(PropertyDescriptor::getPropertyType)
158+
.map(Class::getSimpleName)
159+
.findAny()
160+
.ifPresent(type -> property.addProperty("type", type));
161+
properties.add(property);
162+
}
163+
object.add("properties", properties);
164+
97165
return object;
98166
}
167+
168+
/**
169+
* Gets the necessary properties of a checkstyle module.
170+
* Global properties and undocumented properties are not necessary for us.
171+
* @param clazz the class instance of the given module
172+
* @return a set of the necessary properties of the module
173+
* @throws Exception failure when getting properties
174+
*/
175+
// -@cs[CyclomaticComplexity] many different kinds of module
176+
private static Set<String> getNecessaryProperties(Class<?> clazz)
177+
throws Exception {
178+
final Set<String> properties = getProperties(clazz);
179+
if (hasParentModule(clazz.getSimpleName())) {
180+
if (AbstractJavadocCheck.class.isAssignableFrom(clazz)) {
181+
properties.removeAll(JAVADOC_CHECK_PROPERTIES);
182+
}
183+
else if (ModuleReflectionUtils.isCheckstyleCheck(clazz)) {
184+
properties.removeAll(CHECK_PROPERTIES);
185+
}
186+
}
187+
if (ModuleReflectionUtils.isFileSetModule(clazz)) {
188+
properties.removeAll(FILESET_PROPERTIES);
189+
190+
// override
191+
properties.add("fileExtensions");
192+
}
193+
194+
// undocumented properties are not necessary
195+
properties.removeIf(prop -> UNDOCUMENTED_PROPERTIES.contains(
196+
clazz.getSimpleName() + "." + prop));
197+
198+
final PackageObjectFactory factory = TestUtils.getPackageObjectFactory();
199+
final Object instance = factory.createModule(clazz.getSimpleName());
200+
201+
if (ModuleReflectionUtils.isCheckstyleCheck(clazz)) {
202+
final AbstractCheck check = (AbstractCheck) instance;
203+
204+
final int[] acceptableTokens = check.getAcceptableTokens();
205+
Arrays.sort(acceptableTokens);
206+
final int[] defaultTokens = check.getDefaultTokens();
207+
Arrays.sort(defaultTokens);
208+
final int[] requiredTokens = check.getRequiredTokens();
209+
Arrays.sort(requiredTokens);
210+
211+
if (!Arrays.equals(acceptableTokens, defaultTokens)
212+
|| !Arrays.equals(acceptableTokens, requiredTokens)) {
213+
properties.add("tokens");
214+
}
215+
}
216+
217+
if (AbstractJavadocCheck.class.isAssignableFrom(clazz)) {
218+
final AbstractJavadocCheck check = (AbstractJavadocCheck) instance;
219+
220+
final int[] acceptableJavadocTokens = check.getAcceptableJavadocTokens();
221+
Arrays.sort(acceptableJavadocTokens);
222+
final int[] defaultJavadocTokens = check.getDefaultJavadocTokens();
223+
Arrays.sort(defaultJavadocTokens);
224+
final int[] requiredJavadocTokens = check.getRequiredJavadocTokens();
225+
Arrays.sort(requiredJavadocTokens);
226+
227+
if (!Arrays.equals(acceptableJavadocTokens, defaultJavadocTokens)
228+
|| !Arrays.equals(acceptableJavadocTokens, requiredJavadocTokens)) {
229+
properties.add("javadocTokens");
230+
}
231+
}
232+
233+
return properties;
234+
}
235+
236+
/**
237+
* Gets the properties of a checkstyle module.
238+
* @param clazz the class instance of the given module
239+
* @return a set of the properties of the module
240+
*/
241+
private static Set<String> getProperties(Class<?> clazz) {
242+
final Set<String> result = new TreeSet<>();
243+
final PropertyDescriptor[] map = PropertyUtils.getPropertyDescriptors(clazz);
244+
245+
for (PropertyDescriptor p : map) {
246+
if (p.getWriteMethod() != null) {
247+
result.add(p.getName());
248+
}
249+
}
250+
251+
return result;
252+
}
253+
254+
/**
255+
* Checks whether a module has a parent that may contains global properties.
256+
* @param className the class name of given module
257+
* @return true if the module has a parent
258+
*/
259+
private static boolean hasParentModule(String className) {
260+
return !XML_FILESET_LIST.contains(className) && XML_FILESET_LIST.stream()
261+
.map(name -> name + "Check").noneMatch(name -> name.equals(className));
262+
}
99263
}

src/test/java/com/github/checkstyle/regression/extract/ExtractInfoProcessorTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.junit.Test;
3939

4040
import com.github.checkstyle.regression.data.ImmutableModuleExtractInfo;
41+
import com.github.checkstyle.regression.data.ImmutableModuleProperty;
4142
import com.github.checkstyle.regression.data.ModuleExtractInfo;
4243

4344
public class ExtractInfoProcessorTest {
@@ -69,6 +70,14 @@ public void testGetModuleExtractInfosFromReader() throws Exception {
6970
.name(module1)
7071
.packageName(BASE_PACKAGE + ".checks")
7172
.parent("Checker")
73+
.addProperties(ImmutableModuleProperty.builder()
74+
.name("fileExtensions")
75+
.type("String[]")
76+
.build())
77+
.addProperties(ImmutableModuleProperty.builder()
78+
.name("lineSeparator")
79+
.type("String")
80+
.build())
7281
.build();
7382
final String module2 = "EmptyStatementCheck";
7483
final ModuleExtractInfo extractInfo2 = ImmutableModuleExtractInfo.builder()

src/test/java/com/github/checkstyle/regression/module/ModuleInfoCollectorTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import com.github.checkstyle.regression.data.ImmutableGitChange;
4040
import com.github.checkstyle.regression.data.ImmutableModuleExtractInfo;
4141
import com.github.checkstyle.regression.data.ImmutableModuleInfo;
42+
import com.github.checkstyle.regression.data.ImmutableModuleProperty;
4243
import com.github.checkstyle.regression.data.ModuleExtractInfo;
4344
import com.github.checkstyle.regression.data.ModuleInfo;
4445
import com.github.checkstyle.regression.extract.ExtractInfoProcessor;
@@ -117,6 +118,14 @@ public void testGenerateConfigNodesForValidChanges2() {
117118
.name("NewlineAtEndOfFileCheck")
118119
.packageName(BASE_PACKAGE + ".checks")
119120
.parent("Checker")
121+
.addProperties(ImmutableModuleProperty.builder()
122+
.name("fileExtensions")
123+
.type("String[]")
124+
.build())
125+
.addProperties(ImmutableModuleProperty.builder()
126+
.name("lineSeparator")
127+
.type("String")
128+
.build())
120129
.build();
121130
final List<ModuleInfo> moduleInfos =
122131
ModuleCollector.generate(changes);

src/test/resources/checkstyle_modules.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@
1212
"com.puppycrawl.tools.checkstyle.api.FileSetCheck",
1313
"com.puppycrawl.tools.checkstyle.api.Configurable",
1414
"com.puppycrawl.tools.checkstyle.api.Contextualizable"
15+
],
16+
"properties": [
17+
{
18+
"name": "fileExtensions",
19+
"type": "String[]"
20+
},
21+
{
22+
"name": "lineSeparator",
23+
"type": "String"
24+
}
1525
]
1626
},
1727
{
@@ -26,6 +36,9 @@
2636
"interfaces": [
2737
"com.puppycrawl.tools.checkstyle.api.Configurable",
2838
"com.puppycrawl.tools.checkstyle.api.Contextualizable"
39+
],
40+
"properties": [
41+
2942
]
3043
}
3144
]

0 commit comments

Comments
 (0)