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 * @author bizmailov 028 * 029 */ 030public final class BlockCommentPosition { 031 032 /** 033 * Forbid new instances. 034 */ 035 private BlockCommentPosition() { 036 } 037 038 /** 039 * Node is on type definition. 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 * @param blockComment DetailAST 053 * @return true if node is before class 054 */ 055 public static boolean isOnClass(DetailAST blockComment) { 056 return isOnPlainToken(blockComment, TokenTypes.CLASS_DEF, TokenTypes.LITERAL_CLASS) 057 || isOnTokenWithModifiers(blockComment, TokenTypes.CLASS_DEF) 058 || isOnTokenWithAnnotation(blockComment, TokenTypes.CLASS_DEF); 059 } 060 061 /** 062 * Node is on interface definition. 063 * @param blockComment DetailAST 064 * @return true if node is before interface 065 */ 066 public static boolean isOnInterface(DetailAST blockComment) { 067 return isOnPlainToken(blockComment, TokenTypes.INTERFACE_DEF, TokenTypes.LITERAL_INTERFACE) 068 || isOnTokenWithModifiers(blockComment, TokenTypes.INTERFACE_DEF) 069 || isOnTokenWithAnnotation(blockComment, TokenTypes.INTERFACE_DEF); 070 } 071 072 /** 073 * Node is on enum definition. 074 * @param blockComment DetailAST 075 * @return true if node is before enum 076 */ 077 public static boolean isOnEnum(DetailAST blockComment) { 078 return isOnPlainToken(blockComment, TokenTypes.ENUM_DEF, TokenTypes.ENUM) 079 || isOnTokenWithModifiers(blockComment, TokenTypes.ENUM_DEF) 080 || isOnTokenWithAnnotation(blockComment, TokenTypes.ENUM_DEF); 081 } 082 083 /** 084 * Node is on annotation definition. 085 * @param blockComment DetailAST 086 * @return true if node is before annotation 087 */ 088 public static boolean isOnAnnotationDef(DetailAST blockComment) { 089 return isOnPlainToken(blockComment, TokenTypes.ANNOTATION_DEF, TokenTypes.AT) 090 || isOnTokenWithModifiers(blockComment, TokenTypes.ANNOTATION_DEF) 091 || isOnTokenWithAnnotation(blockComment, TokenTypes.ANNOTATION_DEF); 092 } 093 094 /** 095 * Node is on type member declaration. 096 * @param blockComment DetailAST 097 * @return true if node is before method, field, constructor, enum constant 098 * or annotation field 099 */ 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 260}