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/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));
+ }
}