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}