001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2017 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 class definition. 040 * @param blockComment DetailAST 041 * @return true if node is before class 042 */ 043 public static boolean isOnClass(DetailAST blockComment) { 044 return isOnPlainToken(blockComment, TokenTypes.CLASS_DEF, TokenTypes.LITERAL_CLASS) 045 || isOnTokenWithModifiers(blockComment, TokenTypes.CLASS_DEF) 046 || isOnTokenWithAnnotation(blockComment, TokenTypes.CLASS_DEF); 047 } 048 049 /** 050 * Node is on interface definition. 051 * @param blockComment DetailAST 052 * @return true if node is before interface 053 */ 054 public static boolean isOnInterface(DetailAST blockComment) { 055 return isOnPlainToken(blockComment, TokenTypes.INTERFACE_DEF, TokenTypes.LITERAL_INTERFACE) 056 || isOnTokenWithModifiers(blockComment, TokenTypes.INTERFACE_DEF) 057 || isOnTokenWithAnnotation(blockComment, TokenTypes.INTERFACE_DEF); 058 } 059 060 /** 061 * Node is on enum definition. 062 * @param blockComment DetailAST 063 * @return true if node is before enum 064 */ 065 public static boolean isOnEnum(DetailAST blockComment) { 066 return isOnPlainToken(blockComment, TokenTypes.ENUM_DEF, TokenTypes.ENUM) 067 || isOnTokenWithModifiers(blockComment, TokenTypes.ENUM_DEF) 068 || isOnTokenWithAnnotation(blockComment, TokenTypes.ENUM_DEF); 069 } 070 071 /** 072 * Node is on annotation definition. 073 * @param blockComment DetailAST 074 * @return true if node is before annotation 075 */ 076 public static boolean isOnAnnotationDef(DetailAST blockComment) { 077 return isOnPlainToken(blockComment, TokenTypes.ANNOTATION_DEF, TokenTypes.AT) 078 || isOnTokenWithModifiers(blockComment, TokenTypes.ANNOTATION_DEF) 079 || isOnTokenWithAnnotation(blockComment, TokenTypes.ANNOTATION_DEF); 080 } 081 082 /** 083 * Node is on method declaration. 084 * @param blockComment DetailAST 085 * @return true if node is before method 086 */ 087 public static boolean isOnMethod(DetailAST blockComment) { 088 return isOnPlainClassMember(blockComment, TokenTypes.METHOD_DEF) 089 || isOnTokenWithModifiers(blockComment, TokenTypes.METHOD_DEF) 090 || isOnTokenWithAnnotation(blockComment, TokenTypes.METHOD_DEF); 091 } 092 093 /** 094 * Node is on field declaration. 095 * @param blockComment DetailAST 096 * @return true if node is before field 097 */ 098 public static boolean isOnField(DetailAST blockComment) { 099 return isOnPlainClassMember(blockComment, TokenTypes.VARIABLE_DEF) 100 || isOnTokenWithModifiers(blockComment, TokenTypes.VARIABLE_DEF) 101 || isOnTokenWithAnnotation(blockComment, TokenTypes.VARIABLE_DEF); 102 } 103 104 /** 105 * Node is on constructor. 106 * @param blockComment DetailAST 107 * @return true if node is before constructor 108 */ 109 public static boolean isOnConstructor(DetailAST blockComment) { 110 return isOnPlainToken(blockComment, TokenTypes.CTOR_DEF, TokenTypes.IDENT) 111 || isOnTokenWithModifiers(blockComment, TokenTypes.CTOR_DEF) 112 || isOnTokenWithAnnotation(blockComment, TokenTypes.CTOR_DEF); 113 } 114 115 /** 116 * Node is on enum constant. 117 * @param blockComment DetailAST 118 * @return true if node is before enum constant 119 */ 120 public static boolean isOnEnumConstant(DetailAST blockComment) { 121 final boolean isOnPlainConst = blockComment.getParent() != null 122 && blockComment.getParent().getType() == TokenTypes.ENUM_CONSTANT_DEF 123 && getPrevSiblingSkipComments(blockComment).getType() == TokenTypes.ANNOTATIONS 124 && getPrevSiblingSkipComments(blockComment).getChildCount() == 0; 125 final boolean isOnConstWithAnnotation = !isOnPlainConst && blockComment.getParent() != null 126 && blockComment.getParent().getType() == TokenTypes.ANNOTATION 127 && blockComment.getParent().getParent().getParent().getType() 128 == TokenTypes.ENUM_CONSTANT_DEF; 129 return isOnPlainConst || isOnConstWithAnnotation; 130 } 131 132 /** 133 * Checks that block comment is on specified token without any modifiers. 134 * @param blockComment block comment start DetailAST 135 * @param parentTokenType parent token type 136 * @param nextTokenType next token type 137 * @return true if block comment is on specified token without modifiers 138 */ 139 private static boolean isOnPlainToken(DetailAST blockComment, 140 int parentTokenType, int nextTokenType) { 141 return blockComment.getParent() != null 142 && blockComment.getParent().getType() == parentTokenType 143 && getPrevSiblingSkipComments(blockComment).getChildCount() == 0 144 && getNextSiblingSkipComments(blockComment).getType() == nextTokenType; 145 } 146 147 /** 148 * Checks that block comment is on specified token with modifiers. 149 * @param blockComment block comment start DetailAST 150 * @param tokenType parent token type 151 * @return true if block comment is on specified token with modifiers 152 */ 153 private static boolean isOnTokenWithModifiers(DetailAST blockComment, int tokenType) { 154 return blockComment.getParent() != null 155 && blockComment.getParent().getType() == TokenTypes.MODIFIERS 156 && blockComment.getParent().getParent().getType() == tokenType 157 && getPrevSiblingSkipComments(blockComment) == null; 158 } 159 160 /** 161 * Checks that block comment is on specified token with annotation. 162 * @param blockComment block comment start DetailAST 163 * @param tokenType parent token type 164 * @return true if block comment is on specified token with annotation 165 */ 166 private static boolean isOnTokenWithAnnotation(DetailAST blockComment, int tokenType) { 167 return blockComment.getParent() != null 168 && blockComment.getParent().getType() == TokenTypes.ANNOTATION 169 && getPrevSiblingSkipComments(blockComment.getParent()) == null 170 && blockComment.getParent().getParent().getType() == TokenTypes.MODIFIERS 171 && blockComment.getParent().getParent().getParent().getType() == tokenType 172 && getPrevSiblingSkipComments(blockComment) == null; 173 } 174 175 /** 176 * Checks that block comment is on specified class member without any modifiers. 177 * @param blockComment block comment start DetailAST 178 * @param memberType parent token type 179 * @return true if block comment is on specified token without modifiers 180 */ 181 private static boolean isOnPlainClassMember(DetailAST blockComment, int memberType) { 182 return blockComment.getParent() != null 183 && blockComment.getParent().getType() == TokenTypes.TYPE 184 && blockComment.getParent().getParent().getType() == memberType 185 // previous parent sibling is always TokenTypes.MODIFIERS 186 && blockComment.getParent().getPreviousSibling().getChildCount() == 0; 187 } 188 189 /** 190 * Get next sibling node skipping any comment nodes. 191 * @param node current node 192 * @return next sibling 193 */ 194 private static DetailAST getNextSiblingSkipComments(DetailAST node) { 195 DetailAST result = node.getNextSibling(); 196 while (result.getType() == TokenTypes.SINGLE_LINE_COMMENT 197 || result.getType() == TokenTypes.BLOCK_COMMENT_BEGIN) { 198 result = result.getNextSibling(); 199 } 200 return result; 201 } 202 203 /** 204 * Get previous sibling node skipping any comments. 205 * @param node current node 206 * @return previous sibling 207 */ 208 private static DetailAST getPrevSiblingSkipComments(DetailAST node) { 209 DetailAST result = node.getPreviousSibling(); 210 while (result != null 211 && (result.getType() == TokenTypes.SINGLE_LINE_COMMENT 212 || result.getType() == TokenTypes.BLOCK_COMMENT_BEGIN)) { 213 result = result.getPreviousSibling(); 214 } 215 return result; 216 } 217}