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.xpath; 021 022import com.puppycrawl.tools.checkstyle.api.DetailAST; 023import com.puppycrawl.tools.checkstyle.api.TokenTypes; 024import com.puppycrawl.tools.checkstyle.utils.TokenUtils; 025import net.sf.saxon.om.AxisInfo; 026import net.sf.saxon.om.NodeInfo; 027import net.sf.saxon.tree.iter.ArrayIterator; 028import net.sf.saxon.tree.iter.AxisIterator; 029import net.sf.saxon.tree.iter.EmptyIterator; 030import net.sf.saxon.tree.iter.SingleNodeIterator; 031import net.sf.saxon.tree.util.Navigator; 032import net.sf.saxon.type.Type; 033 034/** 035 * Represents element node of Xpath-tree. 036 * 037 * @author Timur Tibeyev 038 */ 039public class ElementNode extends AbstractNode { 040 041 /** String literal for text attribute. */ 042 private static final String TEXT_ATTRIBUTE_NAME = "text"; 043 044 /** The root node. */ 045 private final AbstractNode root; 046 047 /** The parent of the current node. */ 048 private final AbstractNode parent; 049 050 /** The ast node. */ 051 private final DetailAST detailAst; 052 053 /** Represents text of the DetailAST. */ 054 private final String text; 055 056 /** The attributes. */ 057 private AbstractNode[] attributes; 058 059 /** Represents value of TokenTypes#IDENT. */ 060 private String ident; 061 062 /** 063 * Creates a new {@code ElementNode} instance. 064 * 065 * @param root {@code Node} root of the tree 066 * @param parent {@code Node} parent of the current node 067 * @param detailAst reference to {@code DetailAST} 068 */ 069 public ElementNode(AbstractNode root, AbstractNode parent, DetailAST detailAst) { 070 this.parent = parent; 071 this.root = root; 072 this.detailAst = detailAst; 073 setIdent(); 074 createChildren(); 075 text = TokenUtils.getTokenName(detailAst.getType()); 076 } 077 078 /** 079 * Iterates children of the current node and 080 * recursively creates new Xpath-nodes. 081 */ 082 private void createChildren() { 083 DetailAST currentChild = detailAst.getFirstChild(); 084 while (currentChild != null) { 085 if (currentChild.getType() != TokenTypes.IDENT) { 086 final ElementNode child = new ElementNode(root, this, currentChild); 087 addChild(child); 088 } 089 currentChild = currentChild.getNextSibling(); 090 } 091 } 092 093 /** 094 * Returns attribute value. Throws {@code UnsupportedOperationException} in case, 095 * when name of the attribute is not equal to 'text'. 096 * @param namespace namespace 097 * @param localPart actual name of the attribute 098 * @return attribute value 099 */ 100 @Override 101 public String getAttributeValue(String namespace, String localPart) { 102 if (TEXT_ATTRIBUTE_NAME.equals(localPart)) { 103 return ident; 104 } 105 else { 106 throw throwUnsupportedOperationException(); 107 } 108 } 109 110 /** 111 * Returns local part. 112 * @return local part 113 */ 114 // -@cs[SimpleAccessorNameNotation] Overrides method from the base class. 115 // Issue: https://github.com/sevntu-checkstyle/sevntu.checkstyle/issues/166 116 @Override 117 public String getLocalPart() { 118 return text; 119 } 120 121 /** 122 * Returns type of the node. 123 * @return node kind 124 */ 125 @Override 126 public int getNodeKind() { 127 return Type.ELEMENT; 128 } 129 130 /** 131 * Returns parent. 132 * @return parent 133 */ 134 @Override 135 public NodeInfo getParent() { 136 return parent; 137 } 138 139 /** 140 * Returns root. 141 * @return root 142 */ 143 @Override 144 public NodeInfo getRoot() { 145 return root; 146 } 147 148 /** 149 * Returns string value. 150 * @return string value 151 */ 152 // -@cs[SimpleAccessorNameNotation] Overrides method from the base class. 153 // Issue: https://github.com/sevntu-checkstyle/sevntu.checkstyle/issues/166 154 @Override 155 public String getStringValue() { 156 return text; 157 } 158 159 /** 160 * Determines axis iteration algorithm. Throws {@code UnsupportedOperationException} in case, 161 * when there is no axis iterator for given axisNumber. 162 * 163 * @param axisNumber element from {@code AxisInfo} 164 * @return {@code AxisIterator} object 165 */ 166 @Override 167 public AxisIterator iterateAxis(byte axisNumber) { 168 final AxisIterator result; 169 switch (axisNumber) { 170 case AxisInfo.ANCESTOR: 171 result = new Navigator.AncestorEnumeration(this, false); 172 break; 173 case AxisInfo.ANCESTOR_OR_SELF: 174 result = new Navigator.AncestorEnumeration(this, true); 175 break; 176 case AxisInfo.ATTRIBUTE: 177 if (attributes == null) { 178 result = EmptyIterator.OfNodes.THE_INSTANCE; 179 } 180 else { 181 result = new ArrayIterator.OfNodes(attributes); 182 } 183 break; 184 case AxisInfo.CHILD: 185 if (hasChildNodes()) { 186 result = new ArrayIterator.OfNodes( 187 getChildren().toArray(new AbstractNode[getChildren().size()])); 188 } 189 else { 190 result = EmptyIterator.OfNodes.THE_INSTANCE; 191 } 192 break; 193 case AxisInfo.DESCENDANT: 194 if (hasChildNodes()) { 195 result = new Navigator.DescendantEnumeration(this, false, true); 196 } 197 else { 198 result = EmptyIterator.OfNodes.THE_INSTANCE; 199 } 200 break; 201 case AxisInfo.DESCENDANT_OR_SELF: 202 result = new Navigator.DescendantEnumeration(this, true, true); 203 break; 204 case AxisInfo.PARENT: 205 result = SingleNodeIterator.makeIterator(parent); 206 break; 207 case AxisInfo.SELF: 208 result = SingleNodeIterator.makeIterator(this); 209 break; 210 default: 211 throw throwUnsupportedOperationException(); 212 } 213 return result; 214 } 215 216 /** 217 * Returns line number. 218 * @return line number 219 */ 220 @Override 221 public int getLineNumber() { 222 return detailAst.getLineNo(); 223 } 224 225 /** 226 * Returns column number. 227 * @return column number 228 */ 229 @Override 230 public int getColumnNumber() { 231 return detailAst.getColumnNo(); 232 } 233 234 /** 235 * Getter method for token type. 236 * @return token type 237 */ 238 @Override 239 public int getTokenType() { 240 return detailAst.getType(); 241 } 242 243 /** 244 * Returns underlying node. 245 * @return underlying node 246 */ 247 // -@cs[SimpleAccessorNameNotation] Overrides method from the base class. 248 // Issue: https://github.com/sevntu-checkstyle/sevntu.checkstyle/issues/166 249 @Override 250 public DetailAST getUnderlyingNode() { 251 return detailAst; 252 } 253 254 /** 255 * Finds child element with {@link TokenTypes#IDENT}, extracts its value and stores it. 256 * Value can be accessed using {@code @text} attribute. Now {@code @text} attribute is only 257 * supported attribute. 258 */ 259 private void setIdent() { 260 final DetailAST identAst = detailAst.findFirstToken(TokenTypes.IDENT); 261 if (identAst != null) { 262 ident = identAst.getText(); 263 attributes = new AbstractNode[1]; 264 attributes[0] = new AttributeNode(TEXT_ATTRIBUTE_NAME, ident); 265 } 266 } 267 268 /** 269 * Returns UnsupportedOperationException exception. 270 * @return UnsupportedOperationException exception 271 */ 272 private static UnsupportedOperationException throwUnsupportedOperationException() { 273 return new UnsupportedOperationException("Operation is not supported"); 274 } 275 276}