001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2018 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 interface definition. 062 * @param blockComment DetailAST 063 * @return true if node is before interface 064 */ 065 public static boolean isOnInterface(DetailAST blockComment) { 066 return isOnPlainToken(blockComment, TokenTypes.INTERFACE_DEF, TokenTypes.LITERAL_INTERFACE) 067 || isOnTokenWithModifiers(blockComment, TokenTypes.INTERFACE_DEF) 068 || isOnTokenWithAnnotation(blockComment, TokenTypes.INTERFACE_DEF); 069 } 070 071 /** 072 * Node is on enum definition. 073 * @param blockComment DetailAST 074 * @return true if node is before enum 075 */ 076 public static boolean isOnEnum(DetailAST blockComment) { 077 return isOnPlainToken(blockComment, TokenTypes.ENUM_DEF, TokenTypes.ENUM) 078 || isOnTokenWithModifiers(blockComment, TokenTypes.ENUM_DEF) 079 || isOnTokenWithAnnotation(blockComment, TokenTypes.ENUM_DEF); 080 } 081 082 /** 083 * Node is on annotation definition. 084 * @param blockComment DetailAST 085 * @return true if node is before annotation 086 */ 087 public static boolean isOnAnnotationDef(DetailAST blockComment) { 088 return isOnPlainToken(blockComment, TokenTypes.ANNOTATION_DEF, TokenTypes.AT) 089 || isOnTokenWithModifiers(blockComment, TokenTypes.ANNOTATION_DEF) 090 || isOnTokenWithAnnotation(blockComment, TokenTypes.ANNOTATION_DEF); 091 } 092 093 /** 094 * Node is on type member declaration. 095 * @param blockComment DetailAST 096 * @return true if node is before method, field, constructor, enum constant 097 * or annotation field 098 */ 099 public static boolean isOnMember(DetailAST blockComment) { 100 return isOnMethod(blockComment) 101 || isOnField(blockComment) 102 || isOnConstructor(blockComment) 103 || isOnEnumConstant(blockComment) 104 || isOnAnnotationField(blockComment); 105 } 106 107 /** 108 * Node is on method declaration. 109 * @param blockComment DetailAST 110 * @return true if node is before method 111 */ 112 public static boolean isOnMethod(DetailAST blockComment) { 113 return isOnPlainClassMember(blockComment, TokenTypes.METHOD_DEF) 114 || isOnTokenWithModifiers(blockComment, TokenTypes.METHOD_DEF) 115 || isOnTokenWithAnnotation(blockComment, TokenTypes.METHOD_DEF); 116 } 117 118 /** 119 * Node is on field declaration. 120 * @param blockComment DetailAST 121 * @return true if node is before field 122 */ 123 public static boolean isOnField(DetailAST blockComment) { 124 return isOnPlainClassMember(blockComment, TokenTypes.VARIABLE_DEF) 125 || isOnTokenWithModifiers(blockComment, TokenTypes.VARIABLE_DEF) 126 || isOnTokenWithAnnotation(blockComment, TokenTypes.VARIABLE_DEF); 127 } 128 129 /** 130 * Node is on constructor. 131 * @param blockComment DetailAST 132 * @return true if node is before constructor 133 */ 134 public static boolean isOnConstructor(DetailAST blockComment) { 135 return isOnPlainToken(blockComment, TokenTypes.CTOR_DEF, TokenTypes.IDENT) 136 || isOnTokenWithModifiers(blockComment, TokenTypes.CTOR_DEF) 137 || isOnTokenWithAnnotation(blockComment, TokenTypes.CTOR_DEF); 138 } 139 140 /** 141 * Node is on enum constant. 142 * @param blockComment DetailAST 143 * @return true if node is before enum constant 144 */ 145 public static boolean isOnEnumConstant(DetailAST blockComment) { 146 final boolean isOnPlainConst = blockComment.getParent() != null 147 && blockComment.getParent().getType() == TokenTypes.ENUM_CONSTANT_DEF 148 && getPrevSiblingSkipComments(blockComment).getType() == TokenTypes.ANNOTATIONS 149 && getPrevSiblingSkipComments(blockComment).getChildCount() == 0; 150 final boolean isOnConstWithAnnotation = !isOnPlainConst && blockComment.getParent() != null 151 && blockComment.getParent().getType() == TokenTypes.ANNOTATION 152 && blockComment.getParent().getParent().getParent().getType() 153 == TokenTypes.ENUM_CONSTANT_DEF; 154 return isOnPlainConst || isOnConstWithAnnotation; 155 } 156 157 /** 158 * Node is on annotation field declaration. 159 * @param blockComment DetailAST 160 * @return true if node is before annotation field 161 */ 162 public static boolean isOnAnnotationField(DetailAST blockComment) { 163 return isOnPlainClassMember(blockComment, TokenTypes.ANNOTATION_FIELD_DEF) 164 || isOnTokenWithModifiers(blockComment, TokenTypes.ANNOTATION_FIELD_DEF) 165 || isOnTokenWithAnnotation(blockComment, TokenTypes.ANNOTATION_FIELD_DEF); 166 } 167 168 /** 169 * Checks that block comment is on specified token without any modifiers. 170 * @param blockComment block comment start DetailAST 171 * @param parentTokenType parent token type 172 * @param nextTokenType next token type 173 * @return true if block comment is on specified token without modifiers 174 */ 175 private static boolean isOnPlainToken(DetailAST blockComment, 176 int parentTokenType, int nextTokenType) { 177 return blockComment.getParent() != null 178 && blockComment.getParent().getType() == parentTokenType 179 && getPrevSiblingSkipComments(blockComment).getChildCount() == 0 180 && getNextSiblingSkipComments(blockComment).getType() == nextTokenType; 181 } 182 183 /** 184 * Checks that block comment is on specified token with modifiers. 185 * @param blockComment block comment start DetailAST 186 * @param tokenType parent token type 187 * @return true if block comment is on specified token with modifiers 188 */ 189 private static boolean isOnTokenWithModifiers(DetailAST blockComment, int tokenType) { 190 return blockComment.getParent() != null 191 && blockComment.getParent().getType() == TokenTypes.MODIFIERS 192 && blockComment.getParent().getParent().getType() == tokenType 193 && getPrevSiblingSkipComments(blockComment) == null; 194 } 195 196 /** 197 * Checks that block comment is on specified token with annotation. 198 * @param blockComment block comment start DetailAST 199 * @param tokenType parent token type 200 * @return true if block comment is on specified token with annotation 201 */ 202 private static boolean isOnTokenWithAnnotation(DetailAST blockComment, int tokenType) { 203 return blockComment.getParent() != null 204 && blockComment.getParent().getType() == TokenTypes.ANNOTATION 205 && getPrevSiblingSkipComments(blockComment.getParent()) == null 206 && blockComment.getParent().getParent().getType() == TokenTypes.MODIFIERS 207 && blockComment.getParent().getParent().getParent().getType() == tokenType 208 && getPrevSiblingSkipComments(blockComment) == null; 209 } 210 211 /** 212 * Checks that block comment is on specified class member without any modifiers. 213 * @param blockComment block comment start DetailAST 214 * @param memberType parent token type 215 * @return true if block comment is on specified token without modifiers 216 */ 217 private static boolean isOnPlainClassMember(DetailAST blockComment, int memberType) { 218 DetailAST parent = blockComment.getParent(); 219 // type could be in fully qualified form, so we go up to Type token 220 while (parent != null && parent.getType() == TokenTypes.DOT) { 221 parent = parent.getParent(); 222 } 223 return parent != null 224 && parent.getType() == TokenTypes.TYPE 225 && parent.getParent().getType() == memberType 226 // previous parent sibling is always TokenTypes.MODIFIERS 227 && parent.getPreviousSibling().getChildCount() == 0; 228 } 229 230 /** 231 * Get next sibling node skipping any comment nodes. 232 * @param node current node 233 * @return next sibling 234 */ 235 private static DetailAST getNextSiblingSkipComments(DetailAST node) { 236 DetailAST result = node.getNextSibling(); 237 while (result.getType() == TokenTypes.SINGLE_LINE_COMMENT 238 || result.getType() == TokenTypes.BLOCK_COMMENT_BEGIN) { 239 result = result.getNextSibling(); 240 } 241 return result; 242 } 243 244 /** 245 * Get previous sibling node skipping any comments. 246 * @param node current node 247 * @return previous sibling 248 */ 249 private static DetailAST getPrevSiblingSkipComments(DetailAST node) { 250 DetailAST result = node.getPreviousSibling(); 251 while (result != null 252 && (result.getType() == TokenTypes.SINGLE_LINE_COMMENT 253 || result.getType() == TokenTypes.BLOCK_COMMENT_BEGIN)) { 254 result = result.getPreviousSibling(); 255 } 256 return result; 257 } 258 259}