001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.utils; 021 022import com.puppycrawl.tools.checkstyle.api.DetailAST; 023import com.puppycrawl.tools.checkstyle.api.TokenTypes; 024 025/** 026 * Utility class that has methods to check javadoc comment position in java file. 027 * 028 */ 029public final class BlockCommentPosition { 030 031 /** 032 * Forbid new instances. 033 */ 034 private BlockCommentPosition() { 035 } 036 037 /** 038 * Node is on type definition. 039 * 040 * @param blockComment DetailAST 041 * @return true if node is before class, interface, enum or annotation. 042 */ 043 public static boolean isOnType(DetailAST blockComment) { 044 return isOnClass(blockComment) 045 || isOnInterface(blockComment) 046 || isOnEnum(blockComment) 047 || isOnAnnotationDef(blockComment) 048 || isOnRecord(blockComment); 049 } 050 051 /** 052 * Node is on class definition. 053 * 054 * @param blockComment DetailAST 055 * @return true if node is before class 056 */ 057 public static boolean isOnClass(DetailAST blockComment) { 058 return isOnPlainToken(blockComment, TokenTypes.CLASS_DEF, TokenTypes.LITERAL_CLASS) 059 || isOnTokenWithModifiers(blockComment, TokenTypes.CLASS_DEF) 060 || isOnTokenWithAnnotation(blockComment, TokenTypes.CLASS_DEF); 061 } 062 063 /** 064 * Node is on record definition. 065 * 066 * @param blockComment DetailAST 067 * @return true if node is before class 068 */ 069 public static boolean isOnRecord(DetailAST blockComment) { 070 return isOnPlainToken(blockComment, TokenTypes.RECORD_DEF, TokenTypes.LITERAL_RECORD) 071 || isOnTokenWithModifiers(blockComment, TokenTypes.RECORD_DEF) 072 || isOnTokenWithAnnotation(blockComment, TokenTypes.RECORD_DEF); 073 } 074 075 /** 076 * Node is on package definition. 077 * 078 * @param blockComment DetailAST 079 * @return true if node is before package 080 */ 081 public static boolean isOnPackage(DetailAST blockComment) { 082 boolean result = isOnTokenWithAnnotation(blockComment, TokenTypes.PACKAGE_DEF); 083 084 if (!result) { 085 DetailAST nextSibling = blockComment.getNextSibling(); 086 087 while (nextSibling != null 088 && nextSibling.getType() == TokenTypes.SINGLE_LINE_COMMENT) { 089 nextSibling = nextSibling.getNextSibling(); 090 } 091 092 result = nextSibling != null && nextSibling.getType() == TokenTypes.PACKAGE_DEF; 093 } 094 095 return result; 096 } 097 098 /** 099 * Node is on interface definition. 100 * 101 * @param blockComment DetailAST 102 * @return true if node is before interface 103 */ 104 public static boolean isOnInterface(DetailAST blockComment) { 105 return isOnPlainToken(blockComment, TokenTypes.INTERFACE_DEF, TokenTypes.LITERAL_INTERFACE) 106 || isOnTokenWithModifiers(blockComment, TokenTypes.INTERFACE_DEF) 107 || isOnTokenWithAnnotation(blockComment, TokenTypes.INTERFACE_DEF); 108 } 109 110 /** 111 * Node is on enum definition. 112 * 113 * @param blockComment DetailAST 114 * @return true if node is before enum 115 */ 116 public static boolean isOnEnum(DetailAST blockComment) { 117 return isOnPlainToken(blockComment, TokenTypes.ENUM_DEF, TokenTypes.ENUM) 118 || isOnTokenWithModifiers(blockComment, TokenTypes.ENUM_DEF) 119 || isOnTokenWithAnnotation(blockComment, TokenTypes.ENUM_DEF); 120 } 121 122 /** 123 * Node is on annotation definition. 124 * 125 * @param blockComment DetailAST 126 * @return true if node is before annotation 127 */ 128 public static boolean isOnAnnotationDef(DetailAST blockComment) { 129 return isOnPlainToken(blockComment, TokenTypes.ANNOTATION_DEF, TokenTypes.AT) 130 || isOnTokenWithModifiers(blockComment, TokenTypes.ANNOTATION_DEF) 131 || isOnTokenWithAnnotation(blockComment, TokenTypes.ANNOTATION_DEF); 132 } 133 134 /** 135 * Node is on type member declaration. 136 * 137 * @param blockComment DetailAST 138 * @return true if node is before method, field, constructor, enum constant 139 * or annotation field 140 */ 141 public static boolean isOnMember(DetailAST blockComment) { 142 return isOnMethod(blockComment) 143 || isOnField(blockComment) 144 || isOnConstructor(blockComment) 145 || isOnEnumConstant(blockComment) 146 || isOnAnnotationField(blockComment) 147 || isOnCompactConstructor(blockComment); 148 } 149 150 /** 151 * Node is on method declaration. 152 * 153 * @param blockComment DetailAST 154 * @return true if node is before method 155 */ 156 public static boolean isOnMethod(DetailAST blockComment) { 157 return isOnPlainClassMember(blockComment, TokenTypes.METHOD_DEF) 158 || isOnTokenWithModifiers(blockComment, TokenTypes.METHOD_DEF) 159 || isOnTokenWithAnnotation(blockComment, TokenTypes.METHOD_DEF); 160 } 161 162 /** 163 * Node is on field declaration. 164 * 165 * @param blockComment DetailAST 166 * @return true if node is before field 167 */ 168 public static boolean isOnField(DetailAST blockComment) { 169 return isOnPlainClassMember(blockComment, TokenTypes.VARIABLE_DEF) 170 || isOnTokenWithModifiers(blockComment, TokenTypes.VARIABLE_DEF) 171 && blockComment.getParent().getParent().getParent() 172 .getType() == TokenTypes.OBJBLOCK 173 || isOnTokenWithAnnotation(blockComment, TokenTypes.VARIABLE_DEF) 174 && blockComment.getParent().getParent().getParent() 175 .getParent().getType() == TokenTypes.OBJBLOCK; 176 } 177 178 /** 179 * Node is on constructor. 180 * 181 * @param blockComment DetailAST 182 * @return true if node is before constructor 183 */ 184 public static boolean isOnConstructor(DetailAST blockComment) { 185 return isOnPlainToken(blockComment, TokenTypes.CTOR_DEF, TokenTypes.IDENT) 186 || isOnTokenWithModifiers(blockComment, TokenTypes.CTOR_DEF) 187 || isOnTokenWithAnnotation(blockComment, TokenTypes.CTOR_DEF); 188 } 189 190 /** 191 * Node is on compact constructor, note that we don't need to check for a plain 192 * token here, since a compact constructor must be public. 193 * 194 * @param blockComment DetailAST 195 * @return true if node is before compact constructor 196 */ 197 public static boolean isOnCompactConstructor(DetailAST blockComment) { 198 return isOnTokenWithModifiers(blockComment, TokenTypes.COMPACT_CTOR_DEF) 199 || isOnTokenWithAnnotation(blockComment, TokenTypes.COMPACT_CTOR_DEF); 200 } 201 202 /** 203 * Node is on enum constant. 204 * 205 * @param blockComment DetailAST 206 * @return true if node is before enum constant 207 */ 208 public static boolean isOnEnumConstant(DetailAST blockComment) { 209 final DetailAST parent = blockComment.getParent(); 210 boolean result = false; 211 if (parent != null) { 212 if (parent.getType() == TokenTypes.ENUM_CONSTANT_DEF) { 213 final DetailAST prevSibling = getPrevSiblingSkipComments(blockComment); 214 if (prevSibling.getType() == TokenTypes.ANNOTATIONS && !prevSibling.hasChildren()) { 215 result = true; 216 } 217 } 218 else if (parent.getType() == TokenTypes.ANNOTATION 219 && parent.getParent().getParent().getType() == TokenTypes.ENUM_CONSTANT_DEF) { 220 result = true; 221 } 222 } 223 return result; 224 } 225 226 /** 227 * Node is on annotation field declaration. 228 * 229 * @param blockComment DetailAST 230 * @return true if node is before annotation field 231 */ 232 public static boolean isOnAnnotationField(DetailAST blockComment) { 233 return isOnPlainClassMember(blockComment, TokenTypes.ANNOTATION_FIELD_DEF) 234 || isOnTokenWithModifiers(blockComment, TokenTypes.ANNOTATION_FIELD_DEF) 235 || isOnTokenWithAnnotation(blockComment, TokenTypes.ANNOTATION_FIELD_DEF); 236 } 237 238 /** 239 * Checks that block comment is on specified token without any modifiers. 240 * 241 * @param blockComment block comment start DetailAST 242 * @param parentTokenType parent token type 243 * @param nextTokenType next token type 244 * @return true if block comment is on specified token without modifiers 245 */ 246 private static boolean isOnPlainToken(DetailAST blockComment, 247 int parentTokenType, int nextTokenType) { 248 return blockComment.getParent() != null 249 && blockComment.getParent().getType() == parentTokenType 250 && !getPrevSiblingSkipComments(blockComment).hasChildren() 251 && getNextSiblingSkipComments(blockComment).getType() == nextTokenType; 252 } 253 254 /** 255 * Checks that block comment is on specified token with modifiers. 256 * 257 * @param blockComment block comment start DetailAST 258 * @param tokenType parent token type 259 * @return true if block comment is on specified token with modifiers 260 */ 261 private static boolean isOnTokenWithModifiers(DetailAST blockComment, int tokenType) { 262 return blockComment.getParent() != null 263 && blockComment.getParent().getType() == TokenTypes.MODIFIERS 264 && blockComment.getParent().getParent().getType() == tokenType 265 && getPrevSiblingSkipComments(blockComment) == null; 266 } 267 268 /** 269 * Checks that block comment is on specified token with annotation. 270 * 271 * @param blockComment block comment start DetailAST 272 * @param tokenType parent token type 273 * @return true if block comment is on specified token with annotation 274 */ 275 private static boolean isOnTokenWithAnnotation(DetailAST blockComment, int tokenType) { 276 return blockComment.getParent() != null 277 && blockComment.getParent().getType() == TokenTypes.ANNOTATION 278 && getPrevSiblingSkipComments(blockComment.getParent()) == null 279 && blockComment.getParent().getParent().getParent().getType() == tokenType 280 && getPrevSiblingSkipComments(blockComment) == null; 281 } 282 283 /** 284 * Checks that block comment is on specified class member without any modifiers. 285 * 286 * @param blockComment block comment start DetailAST 287 * @param memberType parent token type 288 * @return true if block comment is on specified token without modifiers 289 */ 290 private static boolean isOnPlainClassMember(DetailAST blockComment, int memberType) { 291 DetailAST parent = blockComment.getParent(); 292 // type could be in fully qualified form, so we go up to Type token 293 while (parent != null && (parent.getType() == TokenTypes.DOT 294 || parent.getType() == TokenTypes.ARRAY_DECLARATOR)) { 295 parent = parent.getParent(); 296 } 297 return parent != null 298 && (parent.getType() == TokenTypes.TYPE 299 || parent.getType() == TokenTypes.TYPE_PARAMETERS) 300 && parent.getParent().getType() == memberType 301 // previous parent sibling is always TokenTypes.MODIFIERS 302 && !parent.getPreviousSibling().hasChildren() 303 && parent.getParent().getParent().getType() == TokenTypes.OBJBLOCK; 304 } 305 306 /** 307 * Get next sibling node skipping any comment nodes. 308 * 309 * @param node current node 310 * @return next sibling 311 */ 312 private static DetailAST getNextSiblingSkipComments(DetailAST node) { 313 DetailAST result = node.getNextSibling(); 314 while (result.getType() == TokenTypes.SINGLE_LINE_COMMENT 315 || result.getType() == TokenTypes.BLOCK_COMMENT_BEGIN) { 316 result = result.getNextSibling(); 317 } 318 return result; 319 } 320 321 /** 322 * Get previous sibling node skipping any comments. 323 * 324 * @param node current node 325 * @return previous sibling 326 */ 327 private static DetailAST getPrevSiblingSkipComments(DetailAST node) { 328 DetailAST result = node.getPreviousSibling(); 329 while (result != null 330 && (result.getType() == TokenTypes.SINGLE_LINE_COMMENT 331 || result.getType() == TokenTypes.BLOCK_COMMENT_BEGIN)) { 332 result = result.getPreviousSibling(); 333 } 334 return result; 335 } 336 337}