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