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 } 049 050 /** 051 * Node is on class definition. 052 * 053 * @param blockComment DetailAST 054 * @return true if node is before class 055 */ 056 public static boolean isOnClass(DetailAST blockComment) { 057 return isOnPlainToken(blockComment, TokenTypes.CLASS_DEF, TokenTypes.LITERAL_CLASS) 058 || isOnTokenWithModifiers(blockComment, TokenTypes.CLASS_DEF) 059 || isOnTokenWithAnnotation(blockComment, TokenTypes.CLASS_DEF); 060 } 061 062 /** 063 * Node is on package definition. 064 * 065 * @param blockComment DetailAST 066 * @return true if node is before package 067 */ 068 public static boolean isOnPackage(DetailAST blockComment) { 069 boolean result = isOnTokenWithAnnotation(blockComment, TokenTypes.PACKAGE_DEF); 070 071 if (!result) { 072 DetailAST nextSibling = blockComment.getNextSibling(); 073 074 while (nextSibling != null 075 && nextSibling.getType() == TokenTypes.SINGLE_LINE_COMMENT) { 076 nextSibling = nextSibling.getNextSibling(); 077 } 078 079 result = nextSibling != null && nextSibling.getType() == TokenTypes.PACKAGE_DEF; 080 } 081 082 return result; 083 } 084 085 /** 086 * Node is on interface definition. 087 * 088 * @param blockComment DetailAST 089 * @return true if node is before interface 090 */ 091 public static boolean isOnInterface(DetailAST blockComment) { 092 return isOnPlainToken(blockComment, TokenTypes.INTERFACE_DEF, TokenTypes.LITERAL_INTERFACE) 093 || isOnTokenWithModifiers(blockComment, TokenTypes.INTERFACE_DEF) 094 || isOnTokenWithAnnotation(blockComment, TokenTypes.INTERFACE_DEF); 095 } 096 097 /** 098 * Node is on enum definition. 099 * 100 * @param blockComment DetailAST 101 * @return true if node is before enum 102 */ 103 public static boolean isOnEnum(DetailAST blockComment) { 104 return isOnPlainToken(blockComment, TokenTypes.ENUM_DEF, TokenTypes.ENUM) 105 || isOnTokenWithModifiers(blockComment, TokenTypes.ENUM_DEF) 106 || isOnTokenWithAnnotation(blockComment, TokenTypes.ENUM_DEF); 107 } 108 109 /** 110 * Node is on annotation definition. 111 * 112 * @param blockComment DetailAST 113 * @return true if node is before annotation 114 */ 115 public static boolean isOnAnnotationDef(DetailAST blockComment) { 116 return isOnPlainToken(blockComment, TokenTypes.ANNOTATION_DEF, TokenTypes.AT) 117 || isOnTokenWithModifiers(blockComment, TokenTypes.ANNOTATION_DEF) 118 || isOnTokenWithAnnotation(blockComment, TokenTypes.ANNOTATION_DEF); 119 } 120 121 /** 122 * Node is on type member declaration. 123 * 124 * @param blockComment DetailAST 125 * @return true if node is before method, field, constructor, enum constant 126 * or annotation field 127 */ 128 public static boolean isOnMember(DetailAST blockComment) { 129 return isOnMethod(blockComment) 130 || isOnField(blockComment) 131 || isOnConstructor(blockComment) 132 || isOnEnumConstant(blockComment) 133 || isOnAnnotationField(blockComment); 134 } 135 136 /** 137 * Node is on method declaration. 138 * 139 * @param blockComment DetailAST 140 * @return true if node is before method 141 */ 142 public static boolean isOnMethod(DetailAST blockComment) { 143 return isOnPlainClassMember(blockComment, TokenTypes.METHOD_DEF) 144 || isOnTokenWithModifiers(blockComment, TokenTypes.METHOD_DEF) 145 || isOnTokenWithAnnotation(blockComment, TokenTypes.METHOD_DEF); 146 } 147 148 /** 149 * Node is on field declaration. 150 * 151 * @param blockComment DetailAST 152 * @return true if node is before field 153 */ 154 public static boolean isOnField(DetailAST blockComment) { 155 return isOnPlainClassMember(blockComment, TokenTypes.VARIABLE_DEF) 156 || isOnTokenWithModifiers(blockComment, TokenTypes.VARIABLE_DEF) 157 && blockComment.getParent().getParent().getParent() 158 .getType() == TokenTypes.OBJBLOCK 159 || isOnTokenWithAnnotation(blockComment, TokenTypes.VARIABLE_DEF) 160 && blockComment.getParent().getParent().getParent() 161 .getParent().getType() == TokenTypes.OBJBLOCK; 162 } 163 164 /** 165 * Node is on constructor. 166 * 167 * @param blockComment DetailAST 168 * @return true if node is before constructor 169 */ 170 public static boolean isOnConstructor(DetailAST blockComment) { 171 return isOnPlainToken(blockComment, TokenTypes.CTOR_DEF, TokenTypes.IDENT) 172 || isOnTokenWithModifiers(blockComment, TokenTypes.CTOR_DEF) 173 || isOnTokenWithAnnotation(blockComment, TokenTypes.CTOR_DEF); 174 } 175 176 /** 177 * Node is on enum constant. 178 * 179 * @param blockComment DetailAST 180 * @return true if node is before enum constant 181 */ 182 public static boolean isOnEnumConstant(DetailAST blockComment) { 183 final DetailAST parent = blockComment.getParent(); 184 boolean result = false; 185 if (parent != null) { 186 if (parent.getType() == TokenTypes.ENUM_CONSTANT_DEF) { 187 final DetailAST prevSibling = getPrevSiblingSkipComments(blockComment); 188 if (prevSibling.getType() == TokenTypes.ANNOTATIONS && !prevSibling.hasChildren()) { 189 result = true; 190 } 191 } 192 else if (parent.getType() == TokenTypes.ANNOTATION 193 && parent.getParent().getParent().getType() == TokenTypes.ENUM_CONSTANT_DEF) { 194 result = true; 195 } 196 } 197 return result; 198 } 199 200 /** 201 * Node is on annotation field declaration. 202 * 203 * @param blockComment DetailAST 204 * @return true if node is before annotation field 205 */ 206 public static boolean isOnAnnotationField(DetailAST blockComment) { 207 return isOnPlainClassMember(blockComment, TokenTypes.ANNOTATION_FIELD_DEF) 208 || isOnTokenWithModifiers(blockComment, TokenTypes.ANNOTATION_FIELD_DEF) 209 || isOnTokenWithAnnotation(blockComment, TokenTypes.ANNOTATION_FIELD_DEF); 210 } 211 212 /** 213 * Checks that block comment is on specified token without any modifiers. 214 * 215 * @param blockComment block comment start DetailAST 216 * @param parentTokenType parent token type 217 * @param nextTokenType next token type 218 * @return true if block comment is on specified token without modifiers 219 */ 220 private static boolean isOnPlainToken(DetailAST blockComment, 221 int parentTokenType, int nextTokenType) { 222 return blockComment.getParent() != null 223 && blockComment.getParent().getType() == parentTokenType 224 && !getPrevSiblingSkipComments(blockComment).hasChildren() 225 && getNextSiblingSkipComments(blockComment).getType() == nextTokenType; 226 } 227 228 /** 229 * Checks that block comment is on specified token with modifiers. 230 * 231 * @param blockComment block comment start DetailAST 232 * @param tokenType parent token type 233 * @return true if block comment is on specified token with modifiers 234 */ 235 private static boolean isOnTokenWithModifiers(DetailAST blockComment, int tokenType) { 236 return blockComment.getParent() != null 237 && blockComment.getParent().getType() == TokenTypes.MODIFIERS 238 && blockComment.getParent().getParent().getType() == tokenType 239 && getPrevSiblingSkipComments(blockComment) == null; 240 } 241 242 /** 243 * Checks that block comment is on specified token with annotation. 244 * 245 * @param blockComment block comment start DetailAST 246 * @param tokenType parent token type 247 * @return true if block comment is on specified token with annotation 248 */ 249 private static boolean isOnTokenWithAnnotation(DetailAST blockComment, int tokenType) { 250 return blockComment.getParent() != null 251 && blockComment.getParent().getType() == TokenTypes.ANNOTATION 252 && getPrevSiblingSkipComments(blockComment.getParent()) == null 253 && blockComment.getParent().getParent().getParent().getType() == tokenType 254 && getPrevSiblingSkipComments(blockComment) == null; 255 } 256 257 /** 258 * Checks that block comment is on specified class member without any modifiers. 259 * 260 * @param blockComment block comment start DetailAST 261 * @param memberType parent token type 262 * @return true if block comment is on specified token without modifiers 263 */ 264 private static boolean isOnPlainClassMember(DetailAST blockComment, int memberType) { 265 DetailAST parent = blockComment.getParent(); 266 // type could be in fully qualified form, so we go up to Type token 267 while (parent != null && (parent.getType() == TokenTypes.DOT 268 || parent.getType() == TokenTypes.ARRAY_DECLARATOR)) { 269 parent = parent.getParent(); 270 } 271 return parent != null 272 && (parent.getType() == TokenTypes.TYPE 273 || parent.getType() == TokenTypes.TYPE_PARAMETERS) 274 && parent.getParent().getType() == memberType 275 // previous parent sibling is always TokenTypes.MODIFIERS 276 && !parent.getPreviousSibling().hasChildren() 277 && parent.getParent().getParent().getType() == TokenTypes.OBJBLOCK; 278 } 279 280 /** 281 * Get next sibling node skipping any comment nodes. 282 * 283 * @param node current node 284 * @return next sibling 285 */ 286 private static DetailAST getNextSiblingSkipComments(DetailAST node) { 287 DetailAST result = node.getNextSibling(); 288 while (result.getType() == TokenTypes.SINGLE_LINE_COMMENT 289 || result.getType() == TokenTypes.BLOCK_COMMENT_BEGIN) { 290 result = result.getNextSibling(); 291 } 292 return result; 293 } 294 295 /** 296 * Get previous sibling node skipping any comments. 297 * 298 * @param node current node 299 * @return previous sibling 300 */ 301 private static DetailAST getPrevSiblingSkipComments(DetailAST node) { 302 DetailAST result = node.getPreviousSibling(); 303 while (result != null 304 && (result.getType() == TokenTypes.SINGLE_LINE_COMMENT 305 || result.getType() == TokenTypes.BLOCK_COMMENT_BEGIN)) { 306 result = result.getPreviousSibling(); 307 } 308 return result; 309 } 310 311}