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