001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2015 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 java.lang.reflect.Field;
023import java.util.ResourceBundle;
024
025import org.apache.commons.lang3.ArrayUtils;
026
027import com.google.common.collect.ImmutableCollection;
028import com.google.common.collect.ImmutableMap;
029import com.puppycrawl.tools.checkstyle.api.TokenTypes;
030
031/**
032 * Contains utility methods for tokens.
033 *
034 * @author <a href="mailto:nesterenko-aleksey@list.ru">Aleksey Nesterenko</a>
035 */
036public final class TokenUtils {
037
038    /** Maps from a token name to value. */
039    private static final ImmutableMap<String, Integer> TOKEN_NAME_TO_VALUE;
040    /** Maps from a token value to name. */
041    private static final String[] TOKEN_VALUE_TO_NAME;
042
043    /** Array of all token IDs. */
044    private static final int[] TOKEN_IDS;
045
046    /** Prefix for exception when getting token by given id. */
047    private static final String TOKEN_ID_EXCEPTION_PREFIX = "given id ";
048
049    /** Prefix for exception when getting token by given name. */
050    private static final String TOKEN_NAME_EXCEPTION_PREFIX = "given name ";
051
052    // initialise the constants
053    static {
054        final ImmutableMap.Builder<String, Integer> builder =
055                ImmutableMap.builder();
056        final Field[] fields = TokenTypes.class.getDeclaredFields();
057        String[] tempTokenValueToName = ArrayUtils.EMPTY_STRING_ARRAY;
058        for (final Field field : fields) {
059            // Only process the int declarations.
060            if (field.getType() != Integer.TYPE) {
061                continue;
062            }
063
064            final String name = field.getName();
065            final int tokenValue = getIntFromField(field, name);
066            builder.put(name, tokenValue);
067            if (tokenValue > tempTokenValueToName.length - 1) {
068                final String[] temp = new String[tokenValue + 1];
069                System.arraycopy(tempTokenValueToName, 0,
070                        temp, 0, tempTokenValueToName.length);
071                tempTokenValueToName = temp;
072            }
073            tempTokenValueToName[tokenValue] = name;
074        }
075
076        TOKEN_NAME_TO_VALUE = builder.build();
077        TOKEN_VALUE_TO_NAME = tempTokenValueToName;
078        final ImmutableCollection<Integer> values = TOKEN_NAME_TO_VALUE.values();
079        final Integer[] ids = values.toArray(new Integer[values.size()]);
080        TOKEN_IDS = ArrayUtils.toPrimitive(ids);
081    }
082
083    /** Stop instances being created. **/
084    private TokenUtils() {
085    }
086
087    /**
088     * Gets the value of a static or instance field of type int or of another primitive type
089     * convertible to type int via a widening conversion. Does not throw any checked exceptions.
090     * @param field from which the int should be extracted
091     * @param object to extract the int value from
092     * @return the value of the field converted to type int
093     * @throws IllegalStateException if this Field object is enforcing Java language access control
094     *         and the underlying field is inaccessible
095     * @see Field#getInt(Object)
096     */
097    public static int getIntFromField(Field field, Object object) {
098        try {
099            return field.getInt(object);
100        }
101        catch (final IllegalAccessException exception) {
102            throw new IllegalStateException(exception);
103        }
104    }
105
106    /**
107     * Get all token IDs that are available in TokenTypes.
108     * @return array of token IDs
109     */
110    public static int[] getAllTokenIds() {
111        final int[] safeCopy = new int[TOKEN_IDS.length];
112        System.arraycopy(TOKEN_IDS, 0, safeCopy, 0, TOKEN_IDS.length);
113        return safeCopy;
114    }
115
116    /**
117     * Returns the name of a token for a given ID.
118     * @param id the ID of the token name to get
119     * @return a token name
120     */
121    public static String getTokenName(int id) {
122        if (id > TOKEN_VALUE_TO_NAME.length - 1) {
123            throw new IllegalArgumentException(TOKEN_ID_EXCEPTION_PREFIX + id);
124        }
125        final String name = TOKEN_VALUE_TO_NAME[id];
126        if (name == null) {
127            throw new IllegalArgumentException(TOKEN_ID_EXCEPTION_PREFIX + id);
128        }
129        return name;
130    }
131
132    /**
133     * Returns the ID of a token for a given name.
134     * @param name the name of the token ID to get
135     * @return a token ID
136     */
137    public static int getTokenId(String name) {
138        final Integer id = TOKEN_NAME_TO_VALUE.get(name);
139        if (id == null) {
140            throw new IllegalArgumentException(TOKEN_NAME_EXCEPTION_PREFIX + name);
141        }
142        return id;
143    }
144
145    /**
146     * Returns the short description of a token for a given name.
147     * @param name the name of the token ID to get
148     * @return a short description
149     */
150    public static String getShortDescription(String name) {
151        if (!TOKEN_NAME_TO_VALUE.containsKey(name)) {
152            throw new IllegalArgumentException(TOKEN_NAME_EXCEPTION_PREFIX + name);
153        }
154
155        final String tokenTypes =
156            "com.puppycrawl.tools.checkstyle.api.tokentypes";
157        final ResourceBundle bundle = ResourceBundle.getBundle(tokenTypes);
158        return bundle.getString(name);
159    }
160
161    /**
162     * Is argument comment-related type (SINGLE_LINE_COMMENT,
163     * BLOCK_COMMENT_BEGIN, BLOCK_COMMENT_END, COMMENT_CONTENT).
164     * @param type
165     *        token type.
166     * @return true if type is comment-related type.
167     */
168    public static boolean isCommentType(int type) {
169        return type == TokenTypes.SINGLE_LINE_COMMENT
170                || type == TokenTypes.BLOCK_COMMENT_BEGIN
171                || type == TokenTypes.BLOCK_COMMENT_END
172                || type == TokenTypes.COMMENT_CONTENT;
173    }
174
175    /**
176     * Is argument comment-related type name (SINGLE_LINE_COMMENT,
177     * BLOCK_COMMENT_BEGIN, BLOCK_COMMENT_END, COMMENT_CONTENT).
178     * @param type
179     *        token type name.
180     * @return true if type is comment-related type name.
181     */
182    public static boolean isCommentType(String type) {
183        return isCommentType(getTokenId(type));
184    }
185
186}