001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2018 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 * @author Oliver Burn
037 * @see TokenTypes#DOT
038 * @see TokenTypes#IDENT
039 **/
040public final class FullIdent {
041
042    /** The list holding subsequent elements of identifier. **/
043    private final List<String> elements = new ArrayList<>();
044    /** The line number. **/
045    private int lineNo;
046    /** The column number. **/
047    private int columnNo;
048
049    /** Hide default constructor. */
050    private FullIdent() {
051    }
052
053    /**
054     * Creates a new FullIdent starting from the specified node.
055     * @param ast the node to start from
056     * @return a {@code FullIdent} value
057     */
058    public static FullIdent createFullIdent(DetailAST ast) {
059        final FullIdent ident = new FullIdent();
060        extractFullIdent(ident, ast);
061        return ident;
062    }
063
064    /**
065     * Creates a new FullIdent starting from the child of the specified node.
066     * @param ast the parent node from where to start from
067     * @return a {@code FullIdent} value
068     */
069    public static FullIdent createFullIdentBelow(DetailAST ast) {
070        return createFullIdent(ast.getFirstChild());
071    }
072
073    /**
074     * Gets the text.
075     * @return the text
076     */
077    public String getText() {
078        return String.join("", elements);
079    }
080
081    /**
082     * Gets the line number.
083     * @return the line number
084     */
085    public int getLineNo() {
086        return lineNo;
087    }
088
089    /**
090     * Gets the column number.
091     * @return the column number
092     */
093    public int getColumnNo() {
094        return columnNo;
095    }
096
097    @Override
098    public String toString() {
099        return String.join("", elements) + "[" + lineNo + "x" + columnNo + "]";
100    }
101
102    /**
103     * Recursively extract a FullIdent.
104     *
105     * @param full the FullIdent to add to
106     * @param ast the node to recurse from
107     */
108    private static void extractFullIdent(FullIdent full, DetailAST ast) {
109        if (ast != null) {
110            if (ast.getType() == TokenTypes.DOT) {
111                extractFullIdent(full, ast.getFirstChild());
112                full.append(".");
113                extractFullIdent(
114                    full, ast.getFirstChild().getNextSibling());
115            }
116            else {
117                full.append(ast);
118            }
119        }
120    }
121
122    /**
123     * Append the specified text.
124     * @param text the text to append
125     */
126    private void append(String text) {
127        elements.add(text);
128    }
129
130    /**
131     * Append the specified token and also recalibrate the first line and
132     * column.
133     * @param ast the token to append
134     */
135    private void append(DetailAST ast) {
136        elements.add(ast.getText());
137        if (lineNo == 0) {
138            lineNo = ast.getLineNo();
139        }
140        else if (ast.getLineNo() > 0) {
141            lineNo = Math.min(lineNo, ast.getLineNo());
142        }
143        if (columnNo == 0) {
144            columnNo = ast.getColumnNo();
145        }
146        else if (ast.getColumnNo() > 0) {
147            columnNo = Math.min(columnNo, ast.getColumnNo());
148        }
149    }
150
151}