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