diff --git a/config/findbugs-exclude.xml b/config/findbugs-exclude.xml
index 0f14231..7040fb5 100644
--- a/config/findbugs-exclude.xml
+++ b/config/findbugs-exclude.xml
@@ -27,4 +27,9 @@
+
+
+
+
+
diff --git a/config/sevntu_suppressions.xml b/config/sevntu_suppressions.xml
index 96c88d8..13a051b 100644
--- a/config/sevntu_suppressions.xml
+++ b/config/sevntu_suppressions.xml
@@ -4,5 +4,5 @@
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
-
+
diff --git a/config/suppressions.xml b/config/suppressions.xml
index fc75af3..523ae74 100644
--- a/config/suppressions.xml
+++ b/config/suppressions.xml
@@ -13,7 +13,7 @@
-
+
diff --git a/src/main/java/com/github/checkstyle/regression/data/ModuleExtractInfo.java b/src/main/java/com/github/checkstyle/regression/data/ModuleExtractInfo.java
index 22566cf..aa7d429 100644
--- a/src/main/java/com/github/checkstyle/regression/data/ModuleExtractInfo.java
+++ b/src/main/java/com/github/checkstyle/regression/data/ModuleExtractInfo.java
@@ -19,6 +19,8 @@
package com.github.checkstyle.regression.data;
+import java.util.List;
+
import org.immutables.gson.Gson;
import org.immutables.value.Value;
@@ -48,6 +50,12 @@ public abstract class ModuleExtractInfo {
*/
public abstract String parent();
+ /**
+ * The properties of this module.
+ * @return the properties of this module
+ */
+ public abstract List properties();
+
/**
* The full qualified name of this module.
* @return the full qualified name of this module
@@ -55,4 +63,33 @@ public abstract class ModuleExtractInfo {
public String fullName() {
return packageName() + "." + name();
}
+
+ /** Represents a property of checkstyle module. */
+ @Gson.TypeAdapters
+ @Value.Immutable
+ public interface ModuleProperty {
+ /**
+ * The name of this property.
+ * @return the name of this property
+ */
+ String name();
+
+ /**
+ * The type of this property.
+ * The value should be one of the followings:
+ * - Pattern
+ * - SeverityLevel
+ * - boolean
+ * - Scope
+ * - double[]
+ * - int[]
+ * - String[]
+ * - String
+ * - URI
+ * - AccessModifier[]
+ * - int
+ * @return the type of this property
+ */
+ String type();
+ }
}
diff --git a/src/main/resources/com/github/checkstyle/regression/extract/ExtractInfoGeneratorTest.java b/src/main/resources/com/github/checkstyle/regression/extract/ExtractInfoGeneratorTest.java
index f7f2790..f135592 100644
--- a/src/main/resources/com/github/checkstyle/regression/extract/ExtractInfoGeneratorTest.java
+++ b/src/main/resources/com/github/checkstyle/regression/extract/ExtractInfoGeneratorTest.java
@@ -19,8 +19,8 @@
package com.puppycrawl.tools.checkstyle;
+import java.beans.PropertyDescriptor;
import java.io.File;
-import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
@@ -28,10 +28,17 @@
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import org.apache.commons.beanutils.PropertyUtils;
import org.junit.Test;
+import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
+import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
+import com.puppycrawl.tools.checkstyle.checks.javadoc.AbstractJavadocCheck;
import com.puppycrawl.tools.checkstyle.internal.CheckUtil;
+import com.puppycrawl.tools.checkstyle.internal.TestUtils;
import com.puppycrawl.tools.checkstyle.utils.ModuleReflectionUtils;
/**
@@ -40,12 +47,57 @@
* @author LuoLiangchen
*/
public class ExtractInfoGeneratorTest {
+ /** Modules which do not have global properties to drop. */
+ private static final List XML_FILESET_LIST = Arrays.asList(
+ "TreeWalker",
+ "Checker",
+ "Header",
+ "Translation",
+ "SeverityMatchFilter",
+ "SuppressionFilter",
+ "SuppressWarningsFilter",
+ "BeforeExecutionExclusionFileFilter",
+ "RegexpHeader",
+ "RegexpOnFilename",
+ "RegexpSingleline",
+ "RegexpMultiline",
+ "JavadocPackage",
+ "NewlineAtEndOfFile",
+ "UniqueProperties",
+ "FileLength",
+ "FileTabCharacter"
+ );
+
+ /** Properties of abstract check. */
+ private static final Set CHECK_PROPERTIES = getProperties(AbstractCheck.class);
+
+ /** Properties of abstract Javadoc check. */
+ private static final Set JAVADOC_CHECK_PROPERTIES =
+ getProperties(AbstractJavadocCheck.class);
+
+ /** Properties of abstract file-set check. */
+ private static final Set FILESET_PROPERTIES = getProperties(AbstractFileSetCheck.class);
+
+ /** Properties without document. */
+ private static final List UNDOCUMENTED_PROPERTIES = Arrays.asList(
+ "Checker.classLoader",
+ "Checker.classloader",
+ "Checker.moduleClassLoader",
+ "Checker.moduleFactory",
+ "TreeWalker.classLoader",
+ "TreeWalker.moduleFactory",
+ "TreeWalker.cacheFile",
+ "TreeWalker.upChild",
+ "SuppressWithNearbyCommentFilter.fileContents",
+ "SuppressionCommentFilter.fileContents"
+ );
+
/**
* Generates the extract info file named as "checkstyle_modules.json".
- * @throws IOException failure when generating the file
+ * @throws Exception failure when generating the file
*/
@Test
- public void generateExtractInfoFile() throws IOException {
+ public void generateExtractInfoFile() throws Exception {
final List> modules = new ArrayList<>(CheckUtil.getCheckstyleModules());
modules.sort(Comparator.comparing(Class::getSimpleName));
final JsonUtil.JsonArray moduleJsonArray = new JsonUtil.JsonArray();
@@ -62,8 +114,10 @@ public void generateExtractInfoFile() throws IOException {
* Creates Json object for a module from the module class.
* @param clazz the given module class
* @return the Json object describing the extract info of the module
+ * @throws Exception failure when creating Json object
*/
- private static JsonUtil.JsonObject createJsonObjectFromModuleClass(Class> clazz) {
+ private static JsonUtil.JsonObject createJsonObjectFromModuleClass(Class> clazz)
+ throws Exception {
final JsonUtil.JsonObject object = new JsonUtil.JsonObject();
final String name = clazz.getSimpleName();
@@ -94,6 +148,116 @@ else if (ModuleReflectionUtils.isRootModule(clazz)) {
object.add("interfaces", interfaces);
object.add("hierarchies", hierarchies);
+ final JsonUtil.JsonArray properties = new JsonUtil.JsonArray();
+ for (String propertyName : getNecessaryProperties(clazz)) {
+ final JsonUtil.JsonObject property = new JsonUtil.JsonObject();
+ property.addProperty("name", propertyName);
+ Arrays.stream(PropertyUtils.getPropertyDescriptors(clazz))
+ .filter(p -> p.getName().equals(propertyName))
+ .map(PropertyDescriptor::getPropertyType)
+ .map(Class::getSimpleName)
+ .findAny()
+ .ifPresent(type -> property.addProperty("type", type));
+ properties.add(property);
+ }
+ object.add("properties", properties);
+
return object;
}
+
+ /**
+ * Gets the necessary properties of a checkstyle module.
+ * Global properties and undocumented properties are not necessary for us.
+ * @param clazz the class instance of the given module
+ * @return a set of the necessary properties of the module
+ * @throws Exception failure when getting properties
+ */
+ // -@cs[CyclomaticComplexity] many different kinds of module
+ private static Set getNecessaryProperties(Class> clazz)
+ throws Exception {
+ final Set properties = getProperties(clazz);
+ if (hasParentModule(clazz.getSimpleName())) {
+ if (AbstractJavadocCheck.class.isAssignableFrom(clazz)) {
+ properties.removeAll(JAVADOC_CHECK_PROPERTIES);
+ }
+ else if (ModuleReflectionUtils.isCheckstyleCheck(clazz)) {
+ properties.removeAll(CHECK_PROPERTIES);
+ }
+ }
+ if (ModuleReflectionUtils.isFileSetModule(clazz)) {
+ properties.removeAll(FILESET_PROPERTIES);
+
+ // override
+ properties.add("fileExtensions");
+ }
+
+ // undocumented properties are not necessary
+ properties.removeIf(prop -> UNDOCUMENTED_PROPERTIES.contains(
+ clazz.getSimpleName() + "." + prop));
+
+ final PackageObjectFactory factory = TestUtils.getPackageObjectFactory();
+ final Object instance = factory.createModule(clazz.getSimpleName());
+
+ if (ModuleReflectionUtils.isCheckstyleCheck(clazz)) {
+ final AbstractCheck check = (AbstractCheck) instance;
+
+ final int[] acceptableTokens = check.getAcceptableTokens();
+ Arrays.sort(acceptableTokens);
+ final int[] defaultTokens = check.getDefaultTokens();
+ Arrays.sort(defaultTokens);
+ final int[] requiredTokens = check.getRequiredTokens();
+ Arrays.sort(requiredTokens);
+
+ if (!Arrays.equals(acceptableTokens, defaultTokens)
+ || !Arrays.equals(acceptableTokens, requiredTokens)) {
+ properties.add("tokens");
+ }
+ }
+
+ if (AbstractJavadocCheck.class.isAssignableFrom(clazz)) {
+ final AbstractJavadocCheck check = (AbstractJavadocCheck) instance;
+
+ final int[] acceptableJavadocTokens = check.getAcceptableJavadocTokens();
+ Arrays.sort(acceptableJavadocTokens);
+ final int[] defaultJavadocTokens = check.getDefaultJavadocTokens();
+ Arrays.sort(defaultJavadocTokens);
+ final int[] requiredJavadocTokens = check.getRequiredJavadocTokens();
+ Arrays.sort(requiredJavadocTokens);
+
+ if (!Arrays.equals(acceptableJavadocTokens, defaultJavadocTokens)
+ || !Arrays.equals(acceptableJavadocTokens, requiredJavadocTokens)) {
+ properties.add("javadocTokens");
+ }
+ }
+
+ return properties;
+ }
+
+ /**
+ * Gets the properties of a checkstyle module.
+ * @param clazz the class instance of the given module
+ * @return a set of the properties of the module
+ */
+ private static Set getProperties(Class> clazz) {
+ final Set result = new TreeSet<>();
+ final PropertyDescriptor[] map = PropertyUtils.getPropertyDescriptors(clazz);
+
+ for (PropertyDescriptor p : map) {
+ if (p.getWriteMethod() != null) {
+ result.add(p.getName());
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Checks whether a module has a parent that may contains global properties.
+ * @param className the class name of given module
+ * @return true if the module has a parent
+ */
+ private static boolean hasParentModule(String className) {
+ return !XML_FILESET_LIST.contains(className) && XML_FILESET_LIST.stream()
+ .map(name -> name + "Check").noneMatch(name -> name.equals(className));
+ }
}
diff --git a/src/test/java/com/github/checkstyle/regression/extract/ExtractInfoProcessorTest.java b/src/test/java/com/github/checkstyle/regression/extract/ExtractInfoProcessorTest.java
index 6f8262a..146c83a 100644
--- a/src/test/java/com/github/checkstyle/regression/extract/ExtractInfoProcessorTest.java
+++ b/src/test/java/com/github/checkstyle/regression/extract/ExtractInfoProcessorTest.java
@@ -38,6 +38,7 @@
import org.junit.Test;
import com.github.checkstyle.regression.data.ImmutableModuleExtractInfo;
+import com.github.checkstyle.regression.data.ImmutableModuleProperty;
import com.github.checkstyle.regression.data.ModuleExtractInfo;
public class ExtractInfoProcessorTest {
@@ -69,6 +70,14 @@ public void testGetModuleExtractInfosFromReader() throws Exception {
.name(module1)
.packageName(BASE_PACKAGE + ".checks")
.parent("Checker")
+ .addProperties(ImmutableModuleProperty.builder()
+ .name("fileExtensions")
+ .type("String[]")
+ .build())
+ .addProperties(ImmutableModuleProperty.builder()
+ .name("lineSeparator")
+ .type("String")
+ .build())
.build();
final String module2 = "EmptyStatementCheck";
final ModuleExtractInfo extractInfo2 = ImmutableModuleExtractInfo.builder()
diff --git a/src/test/java/com/github/checkstyle/regression/module/ModuleInfoCollectorTest.java b/src/test/java/com/github/checkstyle/regression/module/ModuleInfoCollectorTest.java
index ce3466d..a5603e3 100644
--- a/src/test/java/com/github/checkstyle/regression/module/ModuleInfoCollectorTest.java
+++ b/src/test/java/com/github/checkstyle/regression/module/ModuleInfoCollectorTest.java
@@ -39,6 +39,7 @@
import com.github.checkstyle.regression.data.ImmutableGitChange;
import com.github.checkstyle.regression.data.ImmutableModuleExtractInfo;
import com.github.checkstyle.regression.data.ImmutableModuleInfo;
+import com.github.checkstyle.regression.data.ImmutableModuleProperty;
import com.github.checkstyle.regression.data.ModuleExtractInfo;
import com.github.checkstyle.regression.data.ModuleInfo;
import com.github.checkstyle.regression.extract.ExtractInfoProcessor;
@@ -117,6 +118,14 @@ public void testGenerateConfigNodesForValidChanges2() {
.name("NewlineAtEndOfFileCheck")
.packageName(BASE_PACKAGE + ".checks")
.parent("Checker")
+ .addProperties(ImmutableModuleProperty.builder()
+ .name("fileExtensions")
+ .type("String[]")
+ .build())
+ .addProperties(ImmutableModuleProperty.builder()
+ .name("lineSeparator")
+ .type("String")
+ .build())
.build();
final List moduleInfos =
ModuleCollector.generate(changes);
diff --git a/src/test/resources/checkstyle_modules.json b/src/test/resources/checkstyle_modules.json
index 8a28051..6864da0 100644
--- a/src/test/resources/checkstyle_modules.json
+++ b/src/test/resources/checkstyle_modules.json
@@ -12,6 +12,16 @@
"com.puppycrawl.tools.checkstyle.api.FileSetCheck",
"com.puppycrawl.tools.checkstyle.api.Configurable",
"com.puppycrawl.tools.checkstyle.api.Contextualizable"
+ ],
+ "properties": [
+ {
+ "name": "fileExtensions",
+ "type": "String[]"
+ },
+ {
+ "name": "lineSeparator",
+ "type": "String"
+ }
]
},
{
@@ -26,6 +36,9 @@
"interfaces": [
"com.puppycrawl.tools.checkstyle.api.Configurable",
"com.puppycrawl.tools.checkstyle.api.Contextualizable"
+ ],
+ "properties": [
+
]
}
]