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.site;
21
22 import java.beans.Introspector;
23 import java.lang.reflect.Field;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.TreeSet;
29 import java.util.regex.Pattern;
30
31 import org.apache.maven.doxia.macro.MacroExecutionException;
32
33 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
34 import com.puppycrawl.tools.checkstyle.api.DetailAST;
35 import com.puppycrawl.tools.checkstyle.api.DetailNode;
36 import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes;
37 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
38 import com.puppycrawl.tools.checkstyle.checks.javadoc.AbstractJavadocCheck;
39 import com.puppycrawl.tools.checkstyle.utils.BlockCommentPosition;
40 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
41
42
43
44
45
46 @FileStatefulCheck
47 public class ClassAndPropertiesSettersJavadocScraper extends AbstractJavadocCheck {
48
49
50 private static String moduleName = "";
51
52
53 private static Object moduleInstance = new Object();
54
55
56 private static Set<String> properties = Set.of();
57
58
59 private final Map<String, DetailNode> setterNodes = new HashMap<>();
60
61
62
63
64
65
66
67
68 public static void initialize(String newModuleName, Object instance,
69 Set<String> propertiesSet) {
70 JavadocScraperResultUtil.clearData();
71 moduleName = newModuleName;
72 moduleInstance = instance;
73 if (propertiesSet == null) {
74 properties = Set.of();
75 }
76 else {
77 properties = Collections.unmodifiableSet(new TreeSet<>(propertiesSet));
78 }
79 }
80
81 @Override
82 public int[] getDefaultJavadocTokens() {
83 return new int[] {
84 JavadocCommentsTokenTypes.JAVADOC_CONTENT,
85 };
86 }
87
88 @Override
89 public void visitJavadocToken(DetailNode ast) {
90 final DetailAST blockCommentAst = getBlockCommentAst();
91
92 if (BlockCommentPosition.isOnMethod(blockCommentAst)) {
93 handleMethodComment(ast, blockCommentAst);
94 }
95 else if (BlockCommentPosition.isOnField(blockCommentAst)) {
96 handleFieldComment(ast, blockCommentAst);
97 }
98 else if (BlockCommentPosition.isOnClass(blockCommentAst)) {
99 handleClassComment(ast, blockCommentAst);
100 }
101 }
102
103
104
105
106
107
108
109
110 private void handleMethodComment(DetailNode ast, DetailAST blockCommentAst) {
111 final DetailAST methodDef = getParentAst(blockCommentAst, TokenTypes.METHOD_DEF);
112
113 if (methodDef != null
114 && isSetterMethod(methodDef)
115 && isMethodOfScrapedModule(methodDef)) {
116 final String methodName = TokenUtil.getIdent(methodDef).getText();
117 final String propertyName = getPropertyName(methodName);
118 setterNodes.put(propertyName, ast);
119 }
120 }
121
122
123
124
125
126
127
128
129 private void handleFieldComment(DetailNode ast, DetailAST blockCommentAst) {
130 final DetailAST fieldDef = getParentAst(blockCommentAst, TokenTypes.VARIABLE_DEF);
131
132 if (fieldDef != null && isMethodOfScrapedModule(fieldDef)) {
133 final String fieldName = TokenUtil.getIdent(fieldDef).getText();
134 if (isKnownProperty(fieldName)) {
135 setterNodes.put(fieldName, ast);
136 }
137 }
138 }
139
140
141
142
143
144
145
146 private static boolean isKnownProperty(String fieldName) {
147 final boolean result;
148 if (properties.isEmpty()) {
149 result = SiteUtil.VIOLATE_EXECUTION_ON_NON_TIGHT_HTML.equals(fieldName);
150 }
151 else {
152 result = properties.contains(fieldName);
153 }
154 return result;
155 }
156
157
158
159
160
161
162
163
164 private static void handleClassComment(DetailNode ast, DetailAST blockCommentAst) {
165 final DetailAST classDef = getParentAst(blockCommentAst, TokenTypes.CLASS_DEF);
166 if (classDef != null) {
167 final String className = TokenUtil.getIdent(classDef).getText();
168
169 final boolean isModuleNameNotEmpty = moduleName != null && !moduleName.isEmpty();
170
171 final boolean isSameClass = className.equals(moduleName);
172
173 final boolean isModuleInstanceValid = moduleInstance != null
174 && moduleInstance.getClass() != Object.class;
175
176 if (isModuleNameNotEmpty && isSameClass && isModuleInstanceValid) {
177
178 final String moduleSinceVersion =
179 ModuleJavadocParsingUtil.getModuleSinceVersion(ast);
180 JavadocScraperResultUtil.setModuleSinceVersion(moduleSinceVersion);
181
182 final String moduleDescription =
183 ModuleJavadocParsingUtil.getModuleDescription(ast);
184 JavadocScraperResultUtil.setModuleDescription(moduleDescription);
185
186 final String moduleNotes =
187 ModuleJavadocParsingUtil.getModuleNotes(ast);
188 JavadocScraperResultUtil.setModuleNotes(moduleNotes);
189 }
190 }
191 }
192
193 @Override
194 public void finishTree(DetailAST rootAST) {
195 final Set<String> propsToProcess;
196 if (properties.isEmpty()) {
197 propsToProcess = setterNodes.keySet();
198 }
199 else {
200 propsToProcess = properties;
201 }
202
203 for (String property : propsToProcess) {
204 final boolean isRealInstance = moduleInstance != null
205 && moduleInstance.getClass() != Object.class;
206 if (isRealInstance && !setterNodes.containsKey(property)) {
207 continue;
208 }
209 try {
210 final PropertyDetails details = createPropertyDetails(property);
211 JavadocScraperResultUtil.putPropertyDetails(property, details);
212 }
213 catch (MacroExecutionException ignored) {
214
215 }
216 }
217 }
218
219
220
221
222
223
224
225
226
227
228 private static boolean isMethodOfScrapedModule(DetailAST methodDef) {
229 final DetailAST classDef = getParentAst(methodDef, TokenTypes.CLASS_DEF);
230 boolean isMethodOfModule = false;
231 if (classDef != null) {
232 final String className = TokenUtil.getIdent(classDef).getText();
233 isMethodOfModule = className.equals(moduleName);
234 }
235 return isMethodOfModule;
236 }
237
238
239
240
241
242
243
244
245
246 private static DetailAST getParentAst(DetailAST ast, int type) {
247 DetailAST node = ast.getParent();
248 while (node != null && node.getType() != type) {
249 node = node.getParent();
250 }
251 return node;
252 }
253
254
255
256
257
258
259
260
261
262 private static String getPropertyName(String setterName) {
263 return Introspector.decapitalize(setterName.substring("set".length()));
264 }
265
266
267
268
269
270
271
272 private static boolean isSetterMethod(DetailAST ast) {
273 boolean setterMethod = false;
274
275 if (ast.getType() == TokenTypes.METHOD_DEF) {
276 final DetailAST type = ast.findFirstToken(TokenTypes.TYPE);
277 final String name = type.getNextSibling().getText();
278 final Pattern setterPattern = Pattern.compile("^set[A-Z].*");
279
280 setterMethod = setterPattern.matcher(name).matches();
281 }
282 return setterMethod;
283 }
284
285
286
287
288
289
290
291
292 private PropertyDetails createPropertyDetails(String propertyName)
293 throws MacroExecutionException {
294 final DetailNode setterJavadoc = setterNodes.get(propertyName);
295 final Class<?> instanceClass;
296 if (moduleInstance == null) {
297 instanceClass = Object.class;
298 }
299 else {
300 instanceClass = moduleInstance.getClass();
301 }
302
303 final String description = SiteUtil.getPropertyDescriptionForXdoc(propertyName,
304 setterJavadoc, moduleName);
305 final String moduleSinceVersion = JavadocScraperResultUtil.getModuleSinceVersion();
306 final String since = SiteUtil.getPropertySinceVersion(moduleSinceVersion,
307 setterJavadoc);
308
309 final PropertyDetails.Builder builder = new PropertyDetails.Builder()
310 .name(propertyName)
311 .description(description)
312 .sinceVersion(since);
313
314 final boolean isDefaultInstance = moduleInstance == null
315 || moduleInstance.getClass() == Object.class;
316
317 final PropertyDetails result;
318 if (isDefaultInstance) {
319 result = builder.build();
320 }
321 else {
322 final Field field = SiteUtil.getField(instanceClass, propertyName);
323 result = SiteUtil.constructPropertyDetails(builder, moduleInstance, field,
324 propertyName, moduleName);
325 }
326 return result;
327 }
328 }