001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2014 Oliver Burn 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//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle.checks; 020 021import com.google.common.collect.Lists; 022import com.puppycrawl.tools.checkstyle.api.DetailAST; 023import com.puppycrawl.tools.checkstyle.api.FullIdent; 024import com.puppycrawl.tools.checkstyle.api.TokenTypes; 025import java.util.List; 026 027/** 028 * Contains utility methods for the checks. 029 * 030 * @author Oliver Burn 031 * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a> 032 * @author o_sukhodolsky 033 */ 034public final class CheckUtils 035{ 036 /** prevent instances */ 037 private CheckUtils() 038 { 039 throw new UnsupportedOperationException(); 040 } 041 042 /** 043 * Tests whether a method definition AST defines an equals covariant. 044 * @param aAST the method definition AST to test. 045 * Precondition: aAST is a TokenTypes.METHOD_DEF node. 046 * @return true if aAST defines an equals covariant. 047 */ 048 public static boolean isEqualsMethod(DetailAST aAST) 049 { 050 if (aAST.getType() != TokenTypes.METHOD_DEF) { 051 // A node must be method def 052 return false; 053 } 054 055 // non-static, non-abstract? 056 final DetailAST modifiers = aAST.findFirstToken(TokenTypes.MODIFIERS); 057 if (modifiers.branchContains(TokenTypes.LITERAL_STATIC) 058 || modifiers.branchContains(TokenTypes.ABSTRACT)) 059 { 060 return false; 061 } 062 063 // named "equals"? 064 final DetailAST nameNode = aAST.findFirstToken(TokenTypes.IDENT); 065 final String name = nameNode.getText(); 066 if (!"equals".equals(name)) { 067 return false; 068 } 069 070 // one parameter? 071 final DetailAST paramsNode = aAST.findFirstToken(TokenTypes.PARAMETERS); 072 return (paramsNode.getChildCount() == 1); 073 } 074 075 /** 076 * Returns whether a token represents an ELSE as part of an ELSE / IF set. 077 * @param aAST the token to check 078 * @return whether it is 079 */ 080 public static boolean isElseIf(DetailAST aAST) 081 { 082 final DetailAST parentAST = aAST.getParent(); 083 084 return (aAST.getType() == TokenTypes.LITERAL_IF) 085 && (isElse(parentAST) || isElseWithCurlyBraces(parentAST)); 086 } 087 088 /** 089 * Returns whether a token represents an ELSE. 090 * @param aAST the token to check 091 * @return whether the token represents an ELSE 092 */ 093 private static boolean isElse(DetailAST aAST) 094 { 095 return aAST.getType() == TokenTypes.LITERAL_ELSE; 096 } 097 098 /** 099 * Returns whether a token represents an SLIST as part of an ELSE 100 * statement. 101 * @param aAST the token to check 102 * @return whether the toke does represent an SLIST as part of an ELSE 103 */ 104 private static boolean isElseWithCurlyBraces(DetailAST aAST) 105 { 106 return (aAST.getType() == TokenTypes.SLIST) 107 && (aAST.getChildCount() == 2) 108 && isElse(aAST.getParent()); 109 } 110 111 /** 112 * Creates <code>FullIdent</code> for given type node. 113 * @param aTypeAST a type node. 114 * @return <code>FullIdent</code> for given type. 115 */ 116 public static FullIdent createFullType(DetailAST aTypeAST) 117 { 118 final DetailAST arrayDeclAST = 119 aTypeAST.findFirstToken(TokenTypes.ARRAY_DECLARATOR); 120 121 return createFullTypeNoArrays(arrayDeclAST == null ? aTypeAST 122 : arrayDeclAST); 123 } 124 125 /** 126 * @param aTypeAST a type node (no array) 127 * @return <code>FullIdent</code> for given type. 128 */ 129 private static FullIdent createFullTypeNoArrays(DetailAST aTypeAST) 130 { 131 return FullIdent.createFullIdent(aTypeAST.getFirstChild()); 132 } 133 134 // constants for parseDouble() 135 /** octal radix */ 136 private static final int BASE_8 = 8; 137 138 /** decimal radix */ 139 private static final int BASE_10 = 10; 140 141 /** hex radix */ 142 private static final int BASE_16 = 16; 143 144 /** 145 * Returns the value represented by the specified string of the specified 146 * type. Returns 0 for types other than float, double, int, and long. 147 * @param aText the string to be parsed. 148 * @param aType the token type of the text. Should be a constant of 149 * {@link com.puppycrawl.tools.checkstyle.api.TokenTypes}. 150 * @return the double value represented by the string argument. 151 */ 152 public static double parseDouble(String aText, int aType) 153 { 154 String txt = aText.replaceAll("_", ""); 155 double result = 0; 156 switch (aType) { 157 case TokenTypes.NUM_FLOAT: 158 case TokenTypes.NUM_DOUBLE: 159 result = Double.parseDouble(txt); 160 break; 161 case TokenTypes.NUM_INT: 162 case TokenTypes.NUM_LONG: 163 int radix = BASE_10; 164 if (txt.startsWith("0x") || txt.startsWith("0X")) { 165 radix = BASE_16; 166 txt = txt.substring(2); 167 } 168 else if (txt.charAt(0) == '0') { 169 radix = BASE_8; 170 txt = txt.substring(1); 171 } 172 if ((txt.endsWith("L")) || (txt.endsWith("l"))) { 173 txt = txt.substring(0, txt.length() - 1); 174 } 175 if (txt.length() > 0) { 176 if (aType == TokenTypes.NUM_INT) { 177 result = parseInt(txt, radix); 178 } 179 else { 180 result = parseLong(txt, radix); 181 } 182 } 183 break; 184 default: 185 break; 186 } 187 return result; 188 } 189 190 /** 191 * Parses the string argument as a signed integer in the radix specified by 192 * the second argument. The characters in the string must all be digits of 193 * the specified radix. Handles negative values, which method 194 * java.lang.Integer.parseInt(String, int) does not. 195 * @param aText the String containing the integer representation to be 196 * parsed. Precondition: aText contains a parsable int. 197 * @param aRadix the radix to be used while parsing aText. 198 * @return the integer represented by the string argument in the specified 199 * radix. 200 */ 201 public static int parseInt(String aText, int aRadix) 202 { 203 int result = 0; 204 final int max = aText.length(); 205 for (int i = 0; i < max; i++) { 206 final int digit = Character.digit(aText.charAt(i), aRadix); 207 result *= aRadix; 208 result += digit; 209 } 210 return result; 211 } 212 213 /** 214 * Parses the string argument as a signed long in the radix specified by 215 * the second argument. The characters in the string must all be digits of 216 * the specified radix. Handles negative values, which method 217 * java.lang.Integer.parseInt(String, int) does not. 218 * @param aText the String containing the integer representation to be 219 * parsed. Precondition: aText contains a parsable int. 220 * @param aRadix the radix to be used while parsing aText. 221 * @return the long represented by the string argument in the specified 222 * radix. 223 */ 224 public static long parseLong(String aText, int aRadix) 225 { 226 long result = 0; 227 final int max = aText.length(); 228 for (int i = 0; i < max; i++) { 229 final int digit = Character.digit(aText.charAt(i), aRadix); 230 result *= aRadix; 231 result += digit; 232 } 233 return result; 234 } 235 236 /** 237 * Returns the value represented by the specified string of the specified 238 * type. Returns 0 for types other than float, double, int, and long. 239 * @param aText the string to be parsed. 240 * @param aType the token type of the text. Should be a constant of 241 * {@link com.puppycrawl.tools.checkstyle.api.TokenTypes}. 242 * @return the float value represented by the string argument. 243 */ 244 public static double parseFloat(String aText, int aType) 245 { 246 return (float) parseDouble(aText, aType); 247 } 248 249 /** 250 * Finds sub-node for given node minimal (line, column) pair. 251 * @param aNode the root of tree for search. 252 * @return sub-node with minimal (line, column) pair. 253 */ 254 public static DetailAST getFirstNode(final DetailAST aNode) 255 { 256 DetailAST currentNode = aNode; 257 DetailAST child = aNode.getFirstChild(); 258 while (child != null) { 259 final DetailAST newNode = getFirstNode(child); 260 if ((newNode.getLineNo() < currentNode.getLineNo()) 261 || ((newNode.getLineNo() == currentNode.getLineNo()) 262 && (newNode.getColumnNo() < currentNode.getColumnNo()))) 263 { 264 currentNode = newNode; 265 } 266 child = child.getNextSibling(); 267 } 268 269 return currentNode; 270 } 271 272 /** 273 * Retrieves the names of the type parameters to the node. 274 * @param aNode the parameterised AST node 275 * @return a list of type parameter names 276 */ 277 public static List<String> getTypeParameterNames(final DetailAST aNode) 278 { 279 final DetailAST typeParameters = 280 aNode.findFirstToken(TokenTypes.TYPE_PARAMETERS); 281 282 final List<String> typeParamNames = Lists.newArrayList(); 283 if (typeParameters != null) { 284 final DetailAST typeParam = 285 typeParameters.findFirstToken(TokenTypes.TYPE_PARAMETER); 286 typeParamNames.add( 287 typeParam.findFirstToken(TokenTypes.IDENT).getText()); 288 289 DetailAST sibling = typeParam.getNextSibling(); 290 while (sibling != null) { 291 if (sibling.getType() == TokenTypes.TYPE_PARAMETER) { 292 typeParamNames.add( 293 sibling.findFirstToken(TokenTypes.IDENT).getText()); 294 } 295 sibling = sibling.getNextSibling(); 296 } 297 } 298 299 return typeParamNames; 300 } 301 302 /** 303 * Retrieves the type parameters to the node. 304 * @param aNode the parameterised AST node 305 * @return a list of type parameter names 306 */ 307 public static List<DetailAST> getTypeParameters(final DetailAST aNode) 308 { 309 final DetailAST typeParameters = 310 aNode.findFirstToken(TokenTypes.TYPE_PARAMETERS); 311 312 final List<DetailAST> typeParams = Lists.newArrayList(); 313 if (typeParameters != null) { 314 final DetailAST typeParam = 315 typeParameters.findFirstToken(TokenTypes.TYPE_PARAMETER); 316 typeParams.add(typeParam); 317 318 DetailAST sibling = typeParam.getNextSibling(); 319 while (sibling != null) { 320 if (sibling.getType() == TokenTypes.TYPE_PARAMETER) { 321 typeParams.add(sibling); 322 } 323 sibling = sibling.getNextSibling(); 324 } 325 } 326 327 return typeParams; 328 } 329}