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