View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.utils;
21  
22  import com.puppycrawl.tools.checkstyle.api.DetailAST;
23  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
24  
25  /**
26   * Utility class that has methods to check javadoc comment position in java file.
27   * @author bizmailov
28   *
29   */
30  public final class BlockCommentPosition {
31  
32      /**
33       * Forbid new instances.
34       */
35      private BlockCommentPosition() {
36      }
37  
38      /**
39       * Node is on type definition.
40       * @param blockComment DetailAST
41       * @return true if node is before class, interface, enum or annotation.
42       */
43      public static boolean isOnType(DetailAST blockComment) {
44          return isOnClass(blockComment)
45                  || isOnInterface(blockComment)
46                  || isOnEnum(blockComment)
47                  || isOnAnnotationDef(blockComment);
48      }
49  
50      /**
51       * Node is on class definition.
52       * @param blockComment DetailAST
53       * @return true if node is before class
54       */
55      public static boolean isOnClass(DetailAST blockComment) {
56          return isOnPlainToken(blockComment, TokenTypes.CLASS_DEF, TokenTypes.LITERAL_CLASS)
57                  || isOnTokenWithModifiers(blockComment, TokenTypes.CLASS_DEF)
58                  || isOnTokenWithAnnotation(blockComment, TokenTypes.CLASS_DEF);
59      }
60  
61      /**
62       * Node is on interface definition.
63       * @param blockComment DetailAST
64       * @return true if node is before interface
65       */
66      public static boolean isOnInterface(DetailAST blockComment) {
67          return isOnPlainToken(blockComment, TokenTypes.INTERFACE_DEF, TokenTypes.LITERAL_INTERFACE)
68                  || isOnTokenWithModifiers(blockComment, TokenTypes.INTERFACE_DEF)
69                  || isOnTokenWithAnnotation(blockComment, TokenTypes.INTERFACE_DEF);
70      }
71  
72      /**
73       * Node is on enum definition.
74       * @param blockComment DetailAST
75       * @return true if node is before enum
76       */
77      public static boolean isOnEnum(DetailAST blockComment) {
78          return isOnPlainToken(blockComment, TokenTypes.ENUM_DEF, TokenTypes.ENUM)
79                  || isOnTokenWithModifiers(blockComment, TokenTypes.ENUM_DEF)
80                  || isOnTokenWithAnnotation(blockComment, TokenTypes.ENUM_DEF);
81      }
82  
83      /**
84       * Node is on annotation definition.
85       * @param blockComment DetailAST
86       * @return true if node is before annotation
87       */
88      public static boolean isOnAnnotationDef(DetailAST blockComment) {
89          return isOnPlainToken(blockComment, TokenTypes.ANNOTATION_DEF, TokenTypes.AT)
90                  || isOnTokenWithModifiers(blockComment, TokenTypes.ANNOTATION_DEF)
91                  || isOnTokenWithAnnotation(blockComment, TokenTypes.ANNOTATION_DEF);
92      }
93  
94      /**
95       * Node is on type member declaration.
96       * @param blockComment DetailAST
97       * @return true if node is before method, field, constructor, enum constant
98       *     or annotation field
99       */
100     public static boolean isOnMember(DetailAST blockComment) {
101         return isOnMethod(blockComment)
102                 || isOnField(blockComment)
103                 || isOnConstructor(blockComment)
104                 || isOnEnumConstant(blockComment)
105                 || isOnAnnotationField(blockComment);
106     }
107 
108     /**
109      * Node is on method declaration.
110      * @param blockComment DetailAST
111      * @return true if node is before method
112      */
113     public static boolean isOnMethod(DetailAST blockComment) {
114         return isOnPlainClassMember(blockComment, TokenTypes.METHOD_DEF)
115                 || isOnTokenWithModifiers(blockComment, TokenTypes.METHOD_DEF)
116                 || isOnTokenWithAnnotation(blockComment, TokenTypes.METHOD_DEF);
117     }
118 
119     /**
120      * Node is on field declaration.
121      * @param blockComment DetailAST
122      * @return true if node is before field
123      */
124     public static boolean isOnField(DetailAST blockComment) {
125         return isOnPlainClassMember(blockComment, TokenTypes.VARIABLE_DEF)
126                 || isOnTokenWithModifiers(blockComment, TokenTypes.VARIABLE_DEF)
127                 || isOnTokenWithAnnotation(blockComment, TokenTypes.VARIABLE_DEF);
128     }
129 
130     /**
131      * Node is on constructor.
132      * @param blockComment DetailAST
133      * @return true if node is before constructor
134      */
135     public static boolean isOnConstructor(DetailAST blockComment) {
136         return isOnPlainToken(blockComment, TokenTypes.CTOR_DEF, TokenTypes.IDENT)
137                 || isOnTokenWithModifiers(blockComment, TokenTypes.CTOR_DEF)
138                 || isOnTokenWithAnnotation(blockComment, TokenTypes.CTOR_DEF);
139     }
140 
141     /**
142      * Node is on enum constant.
143      * @param blockComment DetailAST
144      * @return true if node is before enum constant
145      */
146     public static boolean isOnEnumConstant(DetailAST blockComment) {
147         final boolean isOnPlainConst = blockComment.getParent() != null
148                 && blockComment.getParent().getType() == TokenTypes.ENUM_CONSTANT_DEF
149                 && getPrevSiblingSkipComments(blockComment).getType() == TokenTypes.ANNOTATIONS
150                 && getPrevSiblingSkipComments(blockComment).getChildCount() == 0;
151         final boolean isOnConstWithAnnotation = !isOnPlainConst && blockComment.getParent() != null
152                 && blockComment.getParent().getType() == TokenTypes.ANNOTATION
153                 && blockComment.getParent().getParent().getParent().getType()
154                     == TokenTypes.ENUM_CONSTANT_DEF;
155         return isOnPlainConst || isOnConstWithAnnotation;
156     }
157 
158     /**
159      * Node is on annotation field declaration.
160      * @param blockComment DetailAST
161      * @return true if node is before annotation field
162      */
163     public static boolean isOnAnnotationField(DetailAST blockComment) {
164         return isOnPlainClassMember(blockComment, TokenTypes.ANNOTATION_FIELD_DEF)
165                 || isOnTokenWithModifiers(blockComment, TokenTypes.ANNOTATION_FIELD_DEF)
166                 || isOnTokenWithAnnotation(blockComment, TokenTypes.ANNOTATION_FIELD_DEF);
167     }
168 
169     /**
170      * Checks that block comment is on specified token without any modifiers.
171      * @param blockComment block comment start DetailAST
172      * @param parentTokenType parent token type
173      * @param nextTokenType next token type
174      * @return true if block comment is on specified token without modifiers
175      */
176     private static boolean isOnPlainToken(DetailAST blockComment,
177             int parentTokenType, int nextTokenType) {
178         return blockComment.getParent() != null
179                 && blockComment.getParent().getType() == parentTokenType
180                 && getPrevSiblingSkipComments(blockComment).getChildCount() == 0
181                 && getNextSiblingSkipComments(blockComment).getType() == nextTokenType;
182     }
183 
184     /**
185      * Checks that block comment is on specified token with modifiers.
186      * @param blockComment block comment start DetailAST
187      * @param tokenType parent token type
188      * @return true if block comment is on specified token with modifiers
189      */
190     private static boolean isOnTokenWithModifiers(DetailAST blockComment, int tokenType) {
191         return blockComment.getParent() != null
192                 && blockComment.getParent().getType() == TokenTypes.MODIFIERS
193                 && blockComment.getParent().getParent().getType() == tokenType
194                 && getPrevSiblingSkipComments(blockComment) == null;
195     }
196 
197     /**
198      * Checks that block comment is on specified token with annotation.
199      * @param blockComment block comment start DetailAST
200      * @param tokenType parent token type
201      * @return true if block comment is on specified token with annotation
202      */
203     private static boolean isOnTokenWithAnnotation(DetailAST blockComment, int tokenType) {
204         return blockComment.getParent() != null
205                 && blockComment.getParent().getType() == TokenTypes.ANNOTATION
206                 && getPrevSiblingSkipComments(blockComment.getParent()) == null
207                 && blockComment.getParent().getParent().getType() == TokenTypes.MODIFIERS
208                 && blockComment.getParent().getParent().getParent().getType() == tokenType
209                 && getPrevSiblingSkipComments(blockComment) == null;
210     }
211 
212     /**
213      * Checks that block comment is on specified class member without any modifiers.
214      * @param blockComment block comment start DetailAST
215      * @param memberType parent token type
216      * @return true if block comment is on specified token without modifiers
217      */
218     private static boolean isOnPlainClassMember(DetailAST blockComment, int memberType) {
219         DetailAST parent = blockComment.getParent();
220         // type could be in fully qualified form, so we go up to Type token
221         while (parent != null && parent.getType() == TokenTypes.DOT) {
222             parent = parent.getParent();
223         }
224         return parent != null
225                 && parent.getType() == TokenTypes.TYPE
226                 && parent.getParent().getType() == memberType
227                 // previous parent sibling is always TokenTypes.MODIFIERS
228                 && parent.getPreviousSibling().getChildCount() == 0;
229     }
230 
231     /**
232      * Get next sibling node skipping any comment nodes.
233      * @param node current node
234      * @return next sibling
235      */
236     private static DetailAST getNextSiblingSkipComments(DetailAST node) {
237         DetailAST result = node.getNextSibling();
238         while (result.getType() == TokenTypes.SINGLE_LINE_COMMENT
239                 || result.getType() == TokenTypes.BLOCK_COMMENT_BEGIN) {
240             result = result.getNextSibling();
241         }
242         return result;
243     }
244 
245     /**
246      * Get previous sibling node skipping any comments.
247      * @param node current node
248      * @return previous sibling
249      */
250     private static DetailAST getPrevSiblingSkipComments(DetailAST node) {
251         DetailAST result = node.getPreviousSibling();
252         while (result != null
253                 && (result.getType() == TokenTypes.SINGLE_LINE_COMMENT
254                 || result.getType() == TokenTypes.BLOCK_COMMENT_BEGIN)) {
255             result = result.getPreviousSibling();
256         }
257         return result;
258     }
259 }