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}