19
19
20
20
package com .puppycrawl .tools .checkstyle ;
21
21
22
+ import java .beans .PropertyDescriptor ;
22
23
import java .io .File ;
23
- import java .io .IOException ;
24
24
import java .nio .charset .Charset ;
25
25
import java .nio .file .Files ;
26
26
import java .nio .file .StandardOpenOption ;
27
27
import java .util .ArrayList ;
28
28
import java .util .Arrays ;
29
29
import java .util .Comparator ;
30
30
import java .util .List ;
31
+ import java .util .Set ;
32
+ import java .util .TreeSet ;
31
33
34
+ import org .apache .commons .beanutils .PropertyUtils ;
32
35
import org .junit .Test ;
33
36
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 ;
34
40
import com .puppycrawl .tools .checkstyle .internal .CheckUtil ;
41
+ import com .puppycrawl .tools .checkstyle .internal .TestUtils ;
35
42
import com .puppycrawl .tools .checkstyle .utils .ModuleReflectionUtils ;
36
43
37
44
/**
40
47
* @author LuoLiangchen
41
48
*/
42
49
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
+
43
95
/**
44
96
* 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
46
98
*/
47
99
@ Test
48
- public void generateExtractInfoFile () throws IOException {
100
+ public void generateExtractInfoFile () throws Exception {
49
101
final List <Class <?>> modules = new ArrayList <>(CheckUtil .getCheckstyleModules ());
50
102
modules .sort (Comparator .comparing (Class ::getSimpleName ));
51
103
final JsonUtil .JsonArray moduleJsonArray = new JsonUtil .JsonArray ();
@@ -62,8 +114,10 @@ public void generateExtractInfoFile() throws IOException {
62
114
* Creates Json object for a module from the module class.
63
115
* @param clazz the given module class
64
116
* @return the Json object describing the extract info of the module
117
+ * @throws Exception failure when creating Json object
65
118
*/
66
- private static JsonUtil .JsonObject createJsonObjectFromModuleClass (Class <?> clazz ) {
119
+ private static JsonUtil .JsonObject createJsonObjectFromModuleClass (Class <?> clazz )
120
+ throws Exception {
67
121
final JsonUtil .JsonObject object = new JsonUtil .JsonObject ();
68
122
69
123
final String name = clazz .getSimpleName ();
@@ -94,6 +148,116 @@ else if (ModuleReflectionUtils.isRootModule(clazz)) {
94
148
object .add ("interfaces" , interfaces );
95
149
object .add ("hierarchies" , hierarchies );
96
150
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
+
97
165
return object ;
98
166
}
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
+ }
99
263
}
0 commit comments