1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.meta;
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.lang.reflect.Field;
25 import java.nio.file.Files;
26 import java.nio.file.Path;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.stream.Stream;
33
34 import javax.xml.parsers.ParserConfigurationException;
35 import javax.xml.transform.TransformerException;
36
37 import org.apache.maven.doxia.macro.MacroExecutionException;
38
39 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
40 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
41 import com.puppycrawl.tools.checkstyle.api.DetailNode;
42 import com.puppycrawl.tools.checkstyle.checks.javadoc.AbstractJavadocCheck;
43 import com.puppycrawl.tools.checkstyle.site.ModuleJavadocParsingUtil;
44 import com.puppycrawl.tools.checkstyle.site.SiteUtil;
45 import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
46 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
47
48
49 public final class MetadataGeneratorUtil {
50
51
52 private MetadataGeneratorUtil() {
53 }
54
55
56
57
58
59
60
61
62
63 public static void generate(String path, String... moduleFolders)
64 throws IOException, CheckstyleException {
65 final List<File> modulesToProcess =
66 getTargetFiles(path, moduleFolders);
67
68 try {
69 for (File file : modulesToProcess) {
70 final String fileName = file.getName();
71
72 if (fileName.startsWith("Abstract")
73 && !"AbstractClassNameCheck.java".equals(fileName)) {
74 continue;
75 }
76
77 final ModuleDetails moduleDetails = getModuleDetails(file);
78 writeMetadataFile(moduleDetails);
79 }
80 }
81 catch (MacroExecutionException macroException) {
82 throw new CheckstyleException(macroException.getMessage(), macroException);
83 }
84 }
85
86
87
88
89
90
91
92
93 private static ModuleDetails getModuleDetails(File file) throws MacroExecutionException {
94 final String moduleName = SiteUtil.FINAL_CHECK.matcher(SiteUtil.getModuleName(file))
95 .replaceAll("");
96
97 final Object instance = SiteUtil.getModuleInstance(moduleName);
98 final Class<?> clss = instance.getClass();
99 final String fullyQualifiedName = clss.getName();
100
101 final String parentModule = SiteUtil.getParentModule(clss);
102 final Object parentModuleInstance = SiteUtil.getModuleInstance(parentModule);
103 final String parentModuleString = parentModuleInstance.getClass().getName();
104
105 final ModuleType moduleType = getModuleType(moduleName);
106
107 final Set<String> messageKeys = SiteUtil.getMessageKeys(clss);
108
109 final String className = SiteUtil.getModuleName(file);
110 final Set<String> properties = SiteUtil.getPropertiesForDocumentation(clss, instance);
111 final Map<String, DetailNode> propertiesJavadocs = SiteUtil
112 .getPropertiesJavadocs(properties, className, file.toPath());
113 final DetailNode moduleJavadoc = SiteUtil.getModuleJavadoc(className, file.toPath());
114 String description = ModuleJavadocParsingUtil
115 .getModuleDescription(moduleJavadoc);
116
117 final String notes = ModuleJavadocParsingUtil.getModuleNotes(moduleJavadoc);
118 if (!notes.isEmpty()) {
119 description = description + "\n\n " + notes;
120 }
121
122 final List<ModulePropertyDetails> propertiesDetails = getPropertiesDetails(
123 properties, propertiesJavadocs, className, instance);
124
125 return new ModuleDetails(moduleName, fullyQualifiedName, parentModuleString,
126 description, moduleType, propertiesDetails, new ArrayList<>(messageKeys));
127 }
128
129
130
131
132
133
134
135 private static ModuleType getModuleType(String moduleName) {
136 final ModuleType result;
137 if (moduleName.endsWith("FileFilter")) {
138 result = ModuleType.FILEFILTER;
139 }
140 else if (moduleName.endsWith("Filter")) {
141 result = ModuleType.FILTER;
142 }
143 else {
144 result = ModuleType.CHECK;
145 }
146 return result;
147 }
148
149
150
151
152
153
154
155
156
157
158
159 private static List<ModulePropertyDetails> getPropertiesDetails(
160 Set<String> properties, Map<String, DetailNode> javadocs,
161 String className, Object instance)
162 throws MacroExecutionException {
163 final List<ModulePropertyDetails> result = new ArrayList<>(properties.size());
164 for (String property : properties) {
165 final String description = getPropertyDescription(property,
166 javadocs.get(property));
167 final Field propertyField = SiteUtil.getField(instance.getClass(), property);
168
169 final String type = SiteUtil.getType(propertyField, property, className, instance);
170
171 final String defaultValue = getPropertyDefaultValue(property, propertyField, instance,
172 className);
173 final String validationType = getValidationType(property, propertyField);
174
175 result.add(new ModulePropertyDetails(property, type, defaultValue,
176 validationType, description));
177 }
178 return result;
179 }
180
181
182
183
184
185
186
187
188
189
190
191 private static String getPropertyDefaultValue(String property, Field field, Object instance,
192 String className) throws MacroExecutionException {
193 final String defaultValue;
194 if (SiteUtil.TOKENS.equals(property)) {
195 final AbstractCheck check = (AbstractCheck) instance;
196 final List<String> configurableTokens = SiteUtil
197 .getDifference(check.getDefaultTokens(),
198 check.getRequiredTokens())
199 .stream()
200 .map(TokenUtil::getTokenName)
201 .toList();
202 defaultValue = String.join(SiteUtil.COMMA, configurableTokens);
203 }
204 else if (SiteUtil.JAVADOC_TOKENS.equals(property)) {
205 final AbstractJavadocCheck check = (AbstractJavadocCheck) instance;
206 final List<String> configurableTokens = SiteUtil
207 .getDifference(check.getDefaultJavadocTokens(),
208 check.getRequiredJavadocTokens())
209 .stream()
210 .map(JavadocUtil::getTokenName)
211 .toList();
212 defaultValue = String.join(SiteUtil.COMMA, configurableTokens);
213 }
214 else {
215 defaultValue = SiteUtil.getDefaultValue(property, field, instance, className);
216 }
217 return defaultValue;
218 }
219
220
221
222
223
224
225
226 private static void writeMetadataFile(ModuleDetails moduleDetails)
227 throws CheckstyleException {
228 try {
229 XmlMetaWriter.write(moduleDetails);
230 }
231 catch (TransformerException | ParserConfigurationException example) {
232 throw new CheckstyleException(
233 "Failed to write metadata into XML file for module: "
234 + moduleDetails.getName(), example);
235 }
236 }
237
238
239
240
241
242
243
244
245 private static String getValidationType(String propertyName, Field propertyField) {
246 final String validationType;
247 if (SiteUtil.TOKENS.equals(propertyName) || SiteUtil.JAVADOC_TOKENS.equals(propertyName)) {
248 validationType = "tokenSet";
249 }
250 else if (propertyField != null
251 && ModuleJavadocParsingUtil.isPropertySpecialTokenProp(propertyField)) {
252 validationType = "tokenTypesSet";
253 }
254 else {
255 validationType = null;
256 }
257 return validationType;
258 }
259
260
261
262
263
264
265
266
267 private static String getPropertyDescription(String property, DetailNode propertyJavadoc) {
268 final String propertyDescription;
269 if (SiteUtil.TOKENS.equals(property)) {
270 propertyDescription = "tokens to check";
271 }
272 else if (SiteUtil.JAVADOC_TOKENS.equals(property)) {
273 propertyDescription = "javadoc tokens to check";
274 }
275 else {
276 final String firstJavadocParagraph = SiteUtil
277 .getFirstParagraphFromJavadoc(propertyJavadoc);
278 final String setterToString = "Setter to ";
279
280 if (firstJavadocParagraph.contains(setterToString)) {
281 final String unprocessedPropertyDescription = firstJavadocParagraph
282 .substring(setterToString.length());
283 final String firstLetterCapitalized = unprocessedPropertyDescription
284 .substring(0, 1)
285 .toUpperCase(Locale.ROOT);
286
287 propertyDescription = firstLetterCapitalized
288 + unprocessedPropertyDescription.substring(1);
289 }
290 else {
291 propertyDescription = firstJavadocParagraph;
292 }
293
294 }
295 return propertyDescription;
296 }
297
298
299
300
301
302
303
304
305
306 private static List<File> getTargetFiles(String path, String... moduleFolders)
307 throws IOException {
308 final List<File> validFiles = new ArrayList<>();
309 for (String folder : moduleFolders) {
310 try (Stream<Path> files = Files.walk(Path.of(path + "/" + folder))) {
311 validFiles.addAll(
312 files.map(Path::toFile)
313 .filter(file -> {
314 final String fileName = file.getName();
315 return fileName.endsWith("SuppressWarningsHolder.java")
316 || fileName.endsWith("Check.java")
317 || fileName.endsWith("Filter.java");
318 })
319 .toList());
320 }
321 }
322
323 return validFiles;
324 }
325 }