001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 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.api;
021
022import java.util.ArrayList;
023import java.util.List;
024
025/**
026 * Represents a full identifier, including dots, with associated
027 * position information.
028 *
029 * <p>
030 * Identifiers such as {@code java.util.HashMap} are spread across
031 * multiple AST nodes in the syntax tree (three IDENT nodes, two DOT nodes).
032 * A FullIdent represents the whole String (excluding any intermediate
033 * whitespace), which is often easier to work with in Checks.
034 * </p>
035 *
036 * @see TokenTypes#DOT
037 * @see TokenTypes#IDENT
038 **/
039public final class FullIdent {
040
041    /** The list holding subsequent elements of identifier. **/
042    private final List<String> elements = new ArrayList<>();
043    /** The topmost and leftmost AST of the full identifier. */
044    private DetailAST detailAst;
045
046    /** Hide default constructor. */
047    private FullIdent() {
048    }
049
050    /**
051     * Creates a new FullIdent starting from the child of the specified node.
052     * @param ast the parent node from where to start from
053     * @return a {@code FullIdent} value
054     */
055    public static FullIdent createFullIdentBelow(DetailAST ast) {
056        return createFullIdent(ast.getFirstChild());
057    }
058
059    /**
060     * Creates a new FullIdent starting from the specified node.
061     *
062     * @param ast the node to start from
063     * @return a {@code FullIdent} value
064     */
065    public static FullIdent createFullIdent(DetailAST ast) {
066        final FullIdent ident = new FullIdent();
067        extractFullIdent(ident, ast);
068        return ident;
069    }
070
071    /**
072     * Recursively extract a FullIdent.
073     *
074     * @param full the FullIdent to add to
075     * @param ast the node to recurse from
076     */
077    private static void extractFullIdent(FullIdent full, DetailAST ast) {
078        if (ast != null) {
079            if (ast.getType() == TokenTypes.DOT) {
080                extractFullIdent(full, ast.getFirstChild());
081                full.append(".");
082                extractFullIdent(
083                    full, ast.getFirstChild().getNextSibling());
084            }
085            else if (ast.getType() == TokenTypes.ARRAY_DECLARATOR) {
086                extractFullIdent(full, ast.getFirstChild());
087                full.append("[]");
088            }
089            else {
090                full.append(ast);
091            }
092        }
093    }
094
095    /**
096     * Gets the text.
097     *
098     * @return the text
099     */
100    public String getText() {
101        return String.join("", elements);
102    }
103
104    /**
105     * Gets the topmost leftmost DetailAST for this FullIdent.
106     *
107     * @return the topmost leftmost ast
108     */
109    public DetailAST getDetailAst() {
110        return detailAst;
111    }
112
113    /**
114     * Gets the line number.
115     *
116     * @return the line number
117     */
118    public int getLineNo() {
119        return detailAst.getLineNo();
120    }
121
122    /**
123     * Gets the column number.
124     *
125     * @return the column number
126     */
127    public int getColumnNo() {
128        return detailAst.getColumnNo();
129    }
130
131    @Override
132    public String toString() {
133        return String.join("", elements)
134            + "[" + detailAst.getLineNo() + "x" + detailAst.getColumnNo() + "]";
135    }
136
137    /**
138     * Append the specified text.
139     *
140     * @param text the text to append
141     */
142    private void append(String text) {
143        elements.add(text);
144    }
145
146    /**
147     * Append the specified token and also recalibrate the first line and
148     * column.
149     *
150     * @param ast the token to append
151     */
152    private void append(DetailAST ast) {
153        elements.add(ast.getText());
154        if (detailAst == null) {
155            detailAst = ast;
156        }
157    }
158
159}