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