2424import com .puppycrawl .tools .checkstyle .JavaParser ;
2525import com .puppycrawl .tools .checkstyle .api .DetailAST ;
2626import com .puppycrawl .tools .checkstyle .api .TokenTypes ;
27+ import com .puppycrawl .tools .checkstyle .utils .TokenUtil ;
2728import com .puppycrawl .tools .checkstyle .utils .XpathUtil ;
2829import io .jooby .Context ;
2930import io .jooby .Router ;
@@ -46,26 +47,31 @@ public Optional<ClassDoc> parse(String typeName) {
4647 return ofNullable (traverse (resolveType (typeName )).get (typeName ));
4748 }
4849
49- public Map <String , ClassDoc > traverse (DetailAST tree ) {
50+ private Map <String , ClassDoc > traverse (DetailAST tree ) {
5051 var classes = new HashMap <String , ClassDoc >();
5152 traverse (
52- tree ,
53+ List . of ( tree ) ,
5354 TYPES ,
5455 modifiers -> tree (modifiers ).noneMatch (tokens (TokenTypes .LITERAL_PRIVATE )),
5556 (scope , comment ) -> {
57+ var scopes = typeHierarchy (scope );
5658 var counter = new AtomicInteger (0 );
5759 counter .addAndGet (comment == JavaDocNode .EMPTY_AST ? 0 : 1 );
5860 var classDoc = new ClassDoc (this , scope , comment );
5961
6062 // MVC routes
6163 traverse (
62- scope ,
64+ scopes ,
6365 tokens (TokenTypes .VARIABLE_DEF , TokenTypes .METHOD_DEF ),
6466 modifiers -> tree (modifiers ).noneMatch (tokens (TokenTypes .LITERAL_STATIC )),
6567 (member , memberComment ) -> {
6668 counter .addAndGet (memberComment == JavaDocNode .EMPTY_AST ? 0 : 1 );
6769 // check member belong to current scope
68- if (scope == backward (member ).filter (TYPES ).findFirst ().orElse (null )) {
70+ if (backward (member )
71+ .filter (TYPES )
72+ .findFirst ()
73+ .filter (scopes ::contains )
74+ .isPresent ()) {
6975 if (member .getType () == TokenTypes .VARIABLE_DEF ) {
7076 classDoc .addField (new FieldDoc (this , member , memberComment ));
7177 } else {
@@ -83,6 +89,53 @@ public Map<String, ClassDoc> traverse(DetailAST tree) {
8389 return classes ;
8490 }
8591
92+ private void traverse (
93+ List <DetailAST > scopes ,
94+ Predicate <DetailAST > types ,
95+ Predicate <DetailAST > modifiers ,
96+ BiConsumer <DetailAST , DetailAST > action ) {
97+
98+ for (var scope : scopes ) {
99+ for (var node : tree (scope ).filter (types ).toList ()) {
100+ var mods =
101+ tree (node )
102+ .filter (tokens (TokenTypes .MODIFIERS ))
103+ .findFirst ()
104+ .orElseThrow (
105+ () ->
106+ new IllegalArgumentException (
107+ "Modifiers not found on " + TokenUtil .getTokenName (node .getType ())));
108+ if (modifiers .test (mods )) {
109+ var docRoot = node .getType () == TokenTypes .VARIABLE_DEF ? mods .getParent () : mods ;
110+ var comment =
111+ tree (docRoot )
112+ .filter (tokens (TokenTypes .BLOCK_COMMENT_BEGIN ))
113+ .findFirst ()
114+ .orElse (JavaDocNode .EMPTY_AST );
115+ action .accept (node , comment );
116+ }
117+ }
118+ }
119+ }
120+
121+ private List <DetailAST > typeHierarchy (DetailAST scope ) {
122+ var result = new ArrayList <DetailAST >();
123+ // call parent members first, so we can inherit and override later.
124+ children (scope )
125+ .filter (tokens (TokenTypes .EXTENDS_CLAUSE ))
126+ .findFirst ()
127+ .map (it -> JavaDocSupport .toQualifiedName (scope , getQualifiedName (it )))
128+ .flatMap (
129+ superClass ->
130+ tree (resolveType (superClass ))
131+ .filter (TYPES )
132+ .filter (it -> superClass .endsWith ("." + getSimpleName (it )))
133+ .findFirst ())
134+ .ifPresent (parent -> result .addAll (typeHierarchy (parent )));
135+ result .add (scope );
136+ return result ;
137+ }
138+
86139 private int scripts (
87140 DetailAST scope , ClassDoc classDoc , PathDoc pathDoc , String prefix , Set <DetailAST > visited ) {
88141 var counter = new AtomicInteger (0 );
@@ -96,7 +149,7 @@ private int scripts(
96149 .map (DetailAST ::getText )
97150 .stream ()
98151 .findFirst ()
99- .orElseThrow (() -> new IllegalStateException ("No method call found: " + script ));
152+ .orElseThrow (() -> new IllegalArgumentException ("No method call found: " + script ));
100153 var scriptComment =
101154 children (script )
102155 .filter (tokens (TokenTypes .BLOCK_COMMENT_BEGIN ))
@@ -254,29 +307,6 @@ private String computePath(String prefix, String pattern) {
254307 return Router .noTrailingSlash (Router .normalizePath (prefix + pattern ));
255308 }
256309
257- private void traverse (
258- DetailAST tree ,
259- Predicate <DetailAST > types ,
260- Predicate <DetailAST > modifiers ,
261- BiConsumer <DetailAST , DetailAST > action ) {
262- for (var node : tree (tree ).filter (types ).toList ()) {
263- var mods =
264- tree (node )
265- .filter (tokens (TokenTypes .MODIFIERS ))
266- .findFirst ()
267- .orElseThrow (() -> new IllegalStateException ("Modifiers not found on " + node ));
268- if (modifiers .test (mods )) {
269- var docRoot = node .getType () == TokenTypes .VARIABLE_DEF ? mods .getParent () : mods ;
270- var comment =
271- tree (docRoot )
272- .filter (tokens (TokenTypes .BLOCK_COMMENT_BEGIN ))
273- .findFirst ()
274- .orElse (JavaDocNode .EMPTY_AST );
275- action .accept (node , comment );
276- }
277- }
278- }
279-
280310 public DetailAST resolve (Path path ) {
281311 return lookup (path )
282312 .map (
0 commit comments