1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 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.api;
21  
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  /**
26   * Represents a full identifier, including dots, with associated
27   * position information.
28   *
29   * <p>
30   * Identifiers such as {@code java.util.HashMap} are spread across
31   * multiple AST nodes in the syntax tree (three IDENT nodes, two DOT nodes).
32   * A FullIdent represents the whole String (excluding any intermediate
33   * whitespace), which is often easier to work with in Checks.
34   * </p>
35   *
36   * @see TokenTypes#DOT
37   * @see TokenTypes#IDENT
38   **/
39  public final class FullIdent {
40  
41      /** The list holding subsequent elements of identifier. **/
42      private final List<String> elements = new ArrayList<>();
43      /** The topmost and leftmost AST of the full identifier. */
44      private DetailAST detailAst;
45  
46      /** Hide default constructor. */
47      private FullIdent() {
48      }
49  
50      /**
51       * Creates a new FullIdent starting from the specified node.
52       * @param ast the node to start from
53       * @return a {@code FullIdent} value
54       */
55      public static FullIdent createFullIdent(DetailAST ast) {
56          final FullIdent ident = new FullIdent();
57          extractFullIdent(ident, ast);
58          return ident;
59      }
60  
61      /**
62       * Creates a new FullIdent starting from the child of the specified node.
63       * @param ast the parent node from where to start from
64       * @return a {@code FullIdent} value
65       */
66      public static FullIdent createFullIdentBelow(DetailAST ast) {
67          return createFullIdent(ast.getFirstChild());
68      }
69  
70      /**
71       * Gets the text.
72       * @return the text
73       */
74      public String getText() {
75          return String.join("", elements);
76      }
77  
78      /**
79       * Gets the topmost leftmost DetailAST for this FullIdent.
80       * @return the topmost leftmost ast
81       */
82      public DetailAST getDetailAst() {
83          return detailAst;
84      }
85  
86      /**
87       * Gets the line number.
88       * @return the line number
89       */
90      public int getLineNo() {
91          return detailAst.getLineNo();
92      }
93  
94      /**
95       * Gets the column number.
96       * @return the column number
97       */
98      public int getColumnNo() {
99          return detailAst.getColumnNo();
100     }
101 
102     @Override
103     public String toString() {
104         return String.join("", elements)
105             + "[" + detailAst.getLineNo() + "x" + detailAst.getColumnNo() + "]";
106     }
107 
108     /**
109      * Recursively extract a FullIdent.
110      *
111      * @param full the FullIdent to add to
112      * @param ast the node to recurse from
113      */
114     private static void extractFullIdent(FullIdent full, DetailAST ast) {
115         if (ast != null) {
116             if (ast.getType() == TokenTypes.DOT) {
117                 extractFullIdent(full, ast.getFirstChild());
118                 full.append(".");
119                 extractFullIdent(
120                     full, ast.getFirstChild().getNextSibling());
121             }
122             else if (ast.getType() == TokenTypes.ARRAY_DECLARATOR) {
123                 extractFullIdent(full, ast.getFirstChild());
124                 full.append("[]");
125             }
126             else {
127                 full.append(ast);
128             }
129         }
130     }
131 
132     /**
133      * Append the specified text.
134      * @param text the text to append
135      */
136     private void append(String text) {
137         elements.add(text);
138     }
139 
140     /**
141      * Append the specified token and also recalibrate the first line and
142      * column.
143      * @param ast the token to append
144      */
145     private void append(DetailAST ast) {
146         elements.add(ast.getText());
147         if (detailAst == null) {
148             detailAst = ast;
149         }
150     }
151 
152 }