001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 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 java.util.List; 023 024import com.puppycrawl.tools.checkstyle.api.DetailAST; 025import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 026import com.puppycrawl.tools.checkstyle.utils.XpathUtil; 027import net.sf.saxon.om.AxisInfo; 028import net.sf.saxon.om.NodeInfo; 029import net.sf.saxon.tree.iter.ArrayIterator; 030import net.sf.saxon.tree.iter.AxisIterator; 031import net.sf.saxon.tree.iter.EmptyIterator; 032import net.sf.saxon.tree.iter.SingleNodeIterator; 033import net.sf.saxon.tree.util.Navigator; 034import net.sf.saxon.type.Type; 035 036/** 037 * Represents element node of Xpath-tree. 038 * 039 */ 040public class ElementNode extends AbstractNode { 041 042 /** String literal for text attribute. */ 043 private static final String TEXT_ATTRIBUTE_NAME = "text"; 044 045 /** Constant for optimization. */ 046 private static final AbstractNode[] EMPTY_ABSTRACT_NODE_ARRAY = new AbstractNode[0]; 047 048 /** The root node. */ 049 private final AbstractNode root; 050 051 /** The parent of the current node. */ 052 private final AbstractNode parent; 053 054 /** The ast node. */ 055 private final DetailAST detailAst; 056 057 /** Represents text of the DetailAST. */ 058 private final String text; 059 060 /** Represents index among siblings. */ 061 private final int indexAmongSiblings; 062 063 /** The text attribute node. */ 064 private AttributeNode attributeNode; 065 066 /** 067 * Creates a new {@code ElementNode} instance. 068 * 069 * @param root {@code Node} root of the tree 070 * @param parent {@code Node} parent of the current node 071 * @param detailAst reference to {@code DetailAST} 072 */ 073 public ElementNode(AbstractNode root, AbstractNode parent, DetailAST detailAst) { 074 super(root.getTreeInfo()); 075 this.parent = parent; 076 this.root = root; 077 this.detailAst = detailAst; 078 text = TokenUtil.getTokenName(detailAst.getType()); 079 indexAmongSiblings = parent.getChildren().size(); 080 setDepth(parent.getDepth() + 1); 081 createTextAttribute(); 082 createChildren(); 083 } 084 085 /** 086 * Compares current object with specified for order. 087 * @param other another {@code NodeInfo} object 088 * @return number representing order of current object to specified one 089 */ 090 @Override 091 public int compareOrder(NodeInfo other) { 092 int result = 0; 093 if (other instanceof AbstractNode) { 094 result = getDepth() - ((AbstractNode) other).getDepth(); 095 if (result == 0) { 096 final ElementNode[] children = getCommonAncestorChildren(other); 097 result = children[0].indexAmongSiblings - children[1].indexAmongSiblings; 098 } 099 } 100 return result; 101 } 102 103 /** 104 * Finds the ancestors of the children whose parent is their common ancestor. 105 * @param other another {@code NodeInfo} object 106 * @return {@code ElementNode} immediate children(also ancestors of the given children) of the 107 * common ancestor 108 */ 109 private ElementNode[] getCommonAncestorChildren(NodeInfo other) { 110 NodeInfo child1 = this; 111 NodeInfo child2 = other; 112 while (!child1.getParent().equals(child2.getParent())) { 113 child1 = child1.getParent(); 114 child2 = child2.getParent(); 115 } 116 return new ElementNode[] {(ElementNode) child1, (ElementNode) child2}; 117 } 118 119 /** 120 * Iterates children of the current node and 121 * recursively creates new Xpath-nodes. 122 */ 123 private void createChildren() { 124 DetailAST currentChild = detailAst.getFirstChild(); 125 while (currentChild != null) { 126 final AbstractNode child = new ElementNode(root, this, currentChild); 127 addChild(child); 128 currentChild = currentChild.getNextSibling(); 129 } 130 } 131 132 /** 133 * Returns attribute value. Throws {@code UnsupportedOperationException} in case, 134 * when name of the attribute is not equal to 'text'. 135 * 136 * @param namespace namespace 137 * @param localPart actual name of the attribute 138 * @return attribute value 139 */ 140 @Override 141 public String getAttributeValue(String namespace, String localPart) { 142 final String result; 143 if (TEXT_ATTRIBUTE_NAME.equals(localPart)) { 144 if (attributeNode == null) { 145 result = null; 146 } 147 else { 148 result = attributeNode.getStringValue(); 149 } 150 } 151 else { 152 result = null; 153 } 154 return result; 155 } 156 157 /** 158 * Returns local part. 159 * 160 * @return local part 161 */ 162 @Override 163 public String getLocalPart() { 164 return text; 165 } 166 167 /** 168 * Returns type of the node. 169 * 170 * @return node kind 171 */ 172 @Override 173 public int getNodeKind() { 174 return Type.ELEMENT; 175 } 176 177 /** 178 * Returns parent. 179 * 180 * @return parent 181 */ 182 @Override 183 public NodeInfo getParent() { 184 return parent; 185 } 186 187 /** 188 * Returns root. 189 * 190 * @return root 191 */ 192 @Override 193 public NodeInfo getRoot() { 194 return root; 195 } 196 197 /** 198 * Determines axis iteration algorithm. Throws {@code UnsupportedOperationException} in case, 199 * when there is no axis iterator for given axisNumber. 200 * 201 * @param axisNumber element from {@code AxisInfo} 202 * @return {@code AxisIterator} object 203 */ 204 @Override 205 public AxisIterator iterateAxis(byte axisNumber) { 206 final AxisIterator result; 207 switch (axisNumber) { 208 case AxisInfo.ANCESTOR: 209 try (AxisIterator iterator = new Navigator.AncestorEnumeration(this, false)) { 210 result = iterator; 211 } 212 break; 213 case AxisInfo.ANCESTOR_OR_SELF: 214 try (AxisIterator iterator = new Navigator.AncestorEnumeration(this, true)) { 215 result = iterator; 216 } 217 break; 218 case AxisInfo.ATTRIBUTE: 219 try (AxisIterator iterator = SingleNodeIterator.makeIterator(attributeNode)) { 220 result = iterator; 221 } 222 break; 223 case AxisInfo.CHILD: 224 if (hasChildNodes()) { 225 try (AxisIterator iterator = new ArrayIterator.OfNodes( 226 getChildren().toArray(EMPTY_ABSTRACT_NODE_ARRAY))) { 227 result = iterator; 228 } 229 } 230 else { 231 result = EmptyIterator.OfNodes.THE_INSTANCE; 232 } 233 break; 234 case AxisInfo.DESCENDANT: 235 if (hasChildNodes()) { 236 try (AxisIterator iterator = 237 new Navigator.DescendantEnumeration(this, false, true)) { 238 result = iterator; 239 } 240 } 241 else { 242 result = EmptyIterator.OfNodes.THE_INSTANCE; 243 } 244 break; 245 case AxisInfo.DESCENDANT_OR_SELF: 246 try (AxisIterator iterator = 247 new Navigator.DescendantEnumeration(this, true, true)) { 248 result = iterator; 249 } 250 break; 251 case AxisInfo.PARENT: 252 try (AxisIterator iterator = SingleNodeIterator.makeIterator(parent)) { 253 result = iterator; 254 } 255 break; 256 case AxisInfo.SELF: 257 try (AxisIterator iterator = SingleNodeIterator.makeIterator(this)) { 258 result = iterator; 259 } 260 break; 261 case AxisInfo.FOLLOWING_SIBLING: 262 result = getFollowingSiblingsIterator(); 263 break; 264 case AxisInfo.PRECEDING_SIBLING: 265 result = getPrecedingSiblingsIterator(); 266 break; 267 case AxisInfo.FOLLOWING: 268 try (AxisIterator iterator = new FollowingEnumeration(this)) { 269 result = iterator; 270 } 271 break; 272 case AxisInfo.PRECEDING: 273 try (AxisIterator iterator = new Navigator.PrecedingEnumeration(this, true)) { 274 result = iterator; 275 } 276 break; 277 default: 278 throw throwUnsupportedOperationException(); 279 } 280 return result; 281 } 282 283 /** 284 * Returns line number. 285 * 286 * @return line number 287 */ 288 @Override 289 public int getLineNumber() { 290 return detailAst.getLineNo(); 291 } 292 293 /** 294 * Returns column number. 295 * 296 * @return column number 297 */ 298 @Override 299 public int getColumnNumber() { 300 return detailAst.getColumnNo(); 301 } 302 303 /** 304 * Getter method for token type. 305 * 306 * @return token type 307 */ 308 @Override 309 public int getTokenType() { 310 return detailAst.getType(); 311 } 312 313 /** 314 * Returns underlying node. 315 * 316 * @return underlying node 317 */ 318 @Override 319 public DetailAST getUnderlyingNode() { 320 return detailAst; 321 } 322 323 /** 324 * Returns preceding sibling axis iterator. 325 * 326 * @return iterator 327 */ 328 private AxisIterator getPrecedingSiblingsIterator() { 329 final AxisIterator result; 330 if (indexAmongSiblings == 0) { 331 result = EmptyIterator.OfNodes.THE_INSTANCE; 332 } 333 else { 334 try (AxisIterator iterator = new ArrayIterator.OfNodes( 335 getPrecedingSiblings().toArray(EMPTY_ABSTRACT_NODE_ARRAY))) { 336 result = iterator; 337 } 338 } 339 return result; 340 } 341 342 /** 343 * Returns following sibling axis iterator. 344 * 345 * @return iterator 346 */ 347 private AxisIterator getFollowingSiblingsIterator() { 348 final AxisIterator result; 349 if (indexAmongSiblings == parent.getChildren().size() - 1) { 350 result = EmptyIterator.OfNodes.THE_INSTANCE; 351 } 352 else { 353 try (AxisIterator iterator = new ArrayIterator.OfNodes( 354 getFollowingSiblings().toArray(EMPTY_ABSTRACT_NODE_ARRAY))) { 355 result = iterator; 356 } 357 } 358 return result; 359 } 360 361 /** 362 * Returns following siblings of the current node. 363 * 364 * @return siblings 365 */ 366 private List<AbstractNode> getFollowingSiblings() { 367 final List<AbstractNode> siblings = parent.getChildren(); 368 return siblings.subList(indexAmongSiblings + 1, siblings.size()); 369 } 370 371 /** 372 * Returns preceding siblings of the current node. 373 * 374 * @return siblings 375 */ 376 private List<AbstractNode> getPrecedingSiblings() { 377 final List<AbstractNode> siblings = parent.getChildren(); 378 return siblings.subList(0, indexAmongSiblings); 379 } 380 381 /** 382 * Checks if token type supports {@code @text} attribute, 383 * extracts its value, creates {@code AttributeNode} object and returns it. 384 * Value can be accessed using {@code @text} attribute. 385 */ 386 private void createTextAttribute() { 387 AttributeNode attribute = null; 388 if (XpathUtil.supportsTextAttribute(detailAst)) { 389 attribute = new AttributeNode(TEXT_ATTRIBUTE_NAME, 390 XpathUtil.getTextAttributeValue(detailAst)); 391 } 392 attributeNode = attribute; 393 } 394 395 /** 396 * Returns UnsupportedOperationException exception. 397 * 398 * @return UnsupportedOperationException exception 399 */ 400 private static UnsupportedOperationException throwUnsupportedOperationException() { 401 return new UnsupportedOperationException("Operation is not supported"); 402 } 403 404 /** 405 * Implementation of the following axis, in terms of the child and following-sibling axes. 406 */ 407 private static final class FollowingEnumeration implements AxisIterator { 408 /** Following-sibling axis iterator. */ 409 private AxisIterator siblingEnum; 410 /** Child axis iterator. */ 411 private AxisIterator descendEnum; 412 413 /** 414 * Create an iterator over the "following" axis. 415 * 416 * @param start the initial context node. 417 */ 418 /* package */ FollowingEnumeration(NodeInfo start) { 419 siblingEnum = start.iterateAxis(AxisInfo.FOLLOWING_SIBLING); 420 } 421 422 /** 423 * Get the next item in the sequence. 424 * 425 * @return the next Item. If there are no more nodes, return null. 426 */ 427 @Override 428 public NodeInfo next() { 429 NodeInfo result = null; 430 if (descendEnum != null) { 431 result = descendEnum.next(); 432 } 433 434 if (result == null) { 435 descendEnum = null; 436 result = siblingEnum.next(); 437 if (result == null) { 438 siblingEnum = null; 439 } 440 else { 441 descendEnum = new Navigator.DescendantEnumeration(result, true, false); 442 result = next(); 443 } 444 } 445 return result; 446 } 447 } 448 449}