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