View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.utils;
21  
22  import java.lang.reflect.Field;
23  import java.util.Locale;
24  import java.util.Optional;
25  import java.util.ResourceBundle;
26  import java.util.function.Predicate;
27  
28  import com.google.common.collect.ImmutableMap;
29  import com.puppycrawl.tools.checkstyle.api.DetailAST;
30  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
31  
32  /**
33   * Contains utility methods for tokens.
34   *
35   * @author <a href="mailto:nesterenko-aleksey@list.ru">Aleksey Nesterenko</a>
36   */
37  public final class TokenUtils {
38  
39      /** Maps from a token name to value. */
40      private static final ImmutableMap<String, Integer> TOKEN_NAME_TO_VALUE;
41      /** Maps from a token value to name. */
42      private static final String[] TOKEN_VALUE_TO_NAME;
43  
44      /** Array of all token IDs. */
45      private static final int[] TOKEN_IDS;
46  
47      /** Prefix for exception when getting token by given id. */
48      private static final String TOKEN_ID_EXCEPTION_PREFIX = "given id ";
49  
50      /** Prefix for exception when getting token by given name. */
51      private static final String TOKEN_NAME_EXCEPTION_PREFIX = "given name ";
52  
53      // initialise the constants
54      static {
55          final ImmutableMap.Builder<String, Integer> builder =
56                  ImmutableMap.builder();
57          final Field[] fields = TokenTypes.class.getDeclaredFields();
58          String[] tempTokenValueToName = CommonUtils.EMPTY_STRING_ARRAY;
59          for (final Field field : fields) {
60              // Only process the int declarations.
61              if (field.getType() != Integer.TYPE) {
62                  continue;
63              }
64  
65              final String name = field.getName();
66              final int tokenValue = getIntFromField(field, name);
67              builder.put(name, tokenValue);
68              if (tokenValue > tempTokenValueToName.length - 1) {
69                  final String[] temp = new String[tokenValue + 1];
70                  System.arraycopy(tempTokenValueToName, 0,
71                          temp, 0, tempTokenValueToName.length);
72                  tempTokenValueToName = temp;
73              }
74              tempTokenValueToName[tokenValue] = name;
75          }
76  
77          TOKEN_NAME_TO_VALUE = builder.build();
78          TOKEN_VALUE_TO_NAME = tempTokenValueToName;
79          TOKEN_IDS = TOKEN_NAME_TO_VALUE.values().stream().mapToInt(Integer::intValue).toArray();
80      }
81  
82      /** Stop instances being created. **/
83      private TokenUtils() {
84      }
85  
86      /**
87       * Gets the value of a static or instance field of type int or of another primitive type
88       * convertible to type int via a widening conversion. Does not throw any checked exceptions.
89       * @param field from which the int should be extracted
90       * @param object to extract the int value from
91       * @return the value of the field converted to type int
92       * @throws IllegalStateException if this Field object is enforcing Java language access control
93       *         and the underlying field is inaccessible
94       * @see Field#getInt(Object)
95       */
96      public static int getIntFromField(Field field, Object object) {
97          try {
98              return field.getInt(object);
99          }
100         catch (final IllegalAccessException exception) {
101             throw new IllegalStateException(exception);
102         }
103     }
104 
105     /**
106      * Get total number of TokenTypes.
107      * @return total number of TokenTypes.
108      */
109     public static int getTokenTypesTotalNumber() {
110         return TOKEN_IDS.length;
111     }
112 
113     /**
114      * Get all token IDs that are available in TokenTypes.
115      * @return array of token IDs
116      */
117     public static int[] getAllTokenIds() {
118         final int[] safeCopy = new int[TOKEN_IDS.length];
119         System.arraycopy(TOKEN_IDS, 0, safeCopy, 0, TOKEN_IDS.length);
120         return safeCopy;
121     }
122 
123     /**
124      * Returns the name of a token for a given ID.
125      * @param id the ID of the token name to get
126      * @return a token name
127      */
128     public static String getTokenName(int id) {
129         if (id > TOKEN_VALUE_TO_NAME.length - 1) {
130             throw new IllegalArgumentException(TOKEN_ID_EXCEPTION_PREFIX + id);
131         }
132         final String name = TOKEN_VALUE_TO_NAME[id];
133         if (name == null) {
134             throw new IllegalArgumentException(TOKEN_ID_EXCEPTION_PREFIX + id);
135         }
136         return name;
137     }
138 
139     /**
140      * Returns the ID of a token for a given name.
141      * @param name the name of the token ID to get
142      * @return a token ID
143      */
144     public static int getTokenId(String name) {
145         final Integer id = TOKEN_NAME_TO_VALUE.get(name);
146         if (id == null) {
147             throw new IllegalArgumentException(TOKEN_NAME_EXCEPTION_PREFIX + name);
148         }
149         return id;
150     }
151 
152     /**
153      * Returns the short description of a token for a given name.
154      * @param name the name of the token ID to get
155      * @return a short description
156      */
157     public static String getShortDescription(String name) {
158         if (!TOKEN_NAME_TO_VALUE.containsKey(name)) {
159             throw new IllegalArgumentException(TOKEN_NAME_EXCEPTION_PREFIX + name);
160         }
161 
162         final String tokenTypes =
163             "com.puppycrawl.tools.checkstyle.api.tokentypes";
164         final ResourceBundle bundle = ResourceBundle.getBundle(tokenTypes, Locale.ROOT);
165         return bundle.getString(name);
166     }
167 
168     /**
169      * Is argument comment-related type (SINGLE_LINE_COMMENT,
170      * BLOCK_COMMENT_BEGIN, BLOCK_COMMENT_END, COMMENT_CONTENT).
171      * @param type
172      *        token type.
173      * @return true if type is comment-related type.
174      */
175     public static boolean isCommentType(int type) {
176         return type == TokenTypes.SINGLE_LINE_COMMENT
177                 || type == TokenTypes.BLOCK_COMMENT_BEGIN
178                 || type == TokenTypes.BLOCK_COMMENT_END
179                 || type == TokenTypes.COMMENT_CONTENT;
180     }
181 
182     /**
183      * Is argument comment-related type name (SINGLE_LINE_COMMENT,
184      * BLOCK_COMMENT_BEGIN, BLOCK_COMMENT_END, COMMENT_CONTENT).
185      * @param type
186      *        token type name.
187      * @return true if type is comment-related type name.
188      */
189     public static boolean isCommentType(String type) {
190         return isCommentType(getTokenId(type));
191     }
192 
193     /**
194      * Finds the first {@link Optional} child token of {@link DetailAST} root node
195      * which matches the given predicate.
196      * @param root root node.
197      * @param predicate predicate.
198      * @return {@link Optional} of {@link DetailAST} node which matches the predicate.
199      */
200     public static Optional<DetailAST> findFirstTokenByPredicate(DetailAST root,
201                                                                 Predicate<DetailAST> predicate) {
202         Optional<DetailAST> result = Optional.empty();
203         for (DetailAST ast = root.getFirstChild(); ast != null; ast = ast.getNextSibling()) {
204             if (predicate.test(ast)) {
205                 result = Optional.of(ast);
206                 break;
207             }
208         }
209         return result;
210     }
211 }