001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 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.Collections; 023import java.util.List; 024import java.util.Optional; 025 026import com.puppycrawl.tools.checkstyle.xpath.iterators.DescendantIterator; 027import com.puppycrawl.tools.checkstyle.xpath.iterators.FollowingIterator; 028import com.puppycrawl.tools.checkstyle.xpath.iterators.PrecedingIterator; 029import com.puppycrawl.tools.checkstyle.xpath.iterators.ReverseListIterator; 030import net.sf.saxon.om.GNode; 031import net.sf.saxon.om.NamespaceUri; 032import net.sf.saxon.om.NodeInfo; 033import net.sf.saxon.om.SequenceIterator; 034import net.sf.saxon.pattern.nodetest.NodePredicate; 035import net.sf.saxon.tree.iter.ArrayIterator; 036import net.sf.saxon.tree.iter.EmptyIterator; 037import net.sf.saxon.tree.util.Navigator; 038import net.sf.saxon.type.Type; 039 040/** 041 * Represents element node of Xpath-tree. 042 */ 043public abstract class AbstractElementNode extends AbstractNode { 044 045 /** String literal for text attribute. */ 046 protected static final String TEXT_ATTRIBUTE_NAME = "text"; 047 048 /** Constant for optimization. */ 049 private static final AbstractNode[] EMPTY_ABSTRACT_NODE_ARRAY = new AbstractNode[0]; 050 051 /** Holder value for lazy creation of attribute node. */ 052 private static final AttributeNode ATTRIBUTE_NODE_UNINITIALIZED = new AttributeNode(null, null); 053 054 /** The root node. */ 055 private final AbstractNode root; 056 057 /** The parent of the current node. */ 058 private final AbstractNode parent; 059 060 /** Depth of the node. */ 061 private final int depth; 062 063 /** Represents index among siblings. */ 064 private final int indexAmongSiblings; 065 066 /** The text attribute node. */ 067 private AttributeNode attributeNode = ATTRIBUTE_NODE_UNINITIALIZED; 068 069 /** 070 * Creates a new {@code AbstractElementNode} instance. 071 * 072 * @param root {@code Node} root of the tree 073 * @param parent {@code Node} parent of the current node 074 * @param depth the current node depth in the hierarchy 075 * @param indexAmongSiblings the current node index among the parent children nodes 076 */ 077 protected AbstractElementNode(AbstractNode root, AbstractNode parent, 078 int depth, int indexAmongSiblings) { 079 super(root.getTreeInfo()); 080 this.parent = parent; 081 this.root = root; 082 this.depth = depth; 083 this.indexAmongSiblings = indexAmongSiblings; 084 } 085 086 /** 087 * Creates {@code AttributeNode} object and returns it. 088 * 089 * @return attribute node if possible, otherwise the {@code null} value 090 */ 091 protected abstract AttributeNode createAttributeNode(); 092 093 /** 094 * Compares current object with specified for order. 095 * 096 * @param other another {@code GNode} object 097 * @return number representing order of current object to specified one 098 */ 099 @Override 100 public int compareOrder(GNode other) { 101 int result = 0; 102 if (other instanceof final AbstractNode node) { 103 result = Integer.compare(depth, node.getDepth()); 104 if (result == 0) { 105 result = compareCommonAncestorChildrenOrder(this, other); 106 } 107 } 108 return result; 109 } 110 111 /** 112 * Walks up the hierarchy until a common ancestor is found. 113 * Then compares topmost sibling nodes. 114 * 115 * @param first {@code GNode} to compare 116 * @param second {@code GNode} to compare 117 * @return the value {@code 0} if {@code first == second}; a value less than 118 * {@code 0} if {@code first} should be first; a value greater than 119 * {@code 0} if {@code second} should be first. 120 */ 121 private static int compareCommonAncestorChildrenOrder(GNode first, GNode second) { 122 GNode child1 = first; 123 GNode child2 = second; 124 while (!child1.getParent().equals(child2.getParent())) { 125 child1 = child1.getParent(); 126 child2 = child2.getParent(); 127 } 128 final int index1 = ((AbstractElementNode) child1).indexAmongSiblings; 129 final int index2 = ((AbstractElementNode) child2).indexAmongSiblings; 130 return Integer.compare(index1, index2); 131 } 132 133 /** 134 * Getter method for node depth. 135 * 136 * @return depth 137 */ 138 @Override 139 public int getDepth() { 140 return depth; 141 } 142 143 /** 144 * Returns attribute value. 145 * 146 * @param namespace namespace 147 * @param localPart actual name of the attribute 148 * @return attribute value or null if the attribute was not found 149 */ 150 @Override 151 public String getAttributeValue(NamespaceUri namespace, String localPart) { 152 final String result; 153 if (TEXT_ATTRIBUTE_NAME.equals(localPart)) { 154 result = Optional.ofNullable(getAttributeNode()) 155 .map(AttributeNode::getStringValue) 156 .orElse(null); 157 } 158 else { 159 result = null; 160 } 161 return result; 162 } 163 164 /** 165 * Returns type of the node. 166 * 167 * @return node kind 168 */ 169 @Override 170 public int getNodeKind() { 171 return Type.ELEMENT; 172 } 173 174 /** 175 * Returns parent. 176 * 177 * @return parent 178 */ 179 @Override 180 public NodeInfo getParent() { 181 return parent; 182 } 183 184 /** 185 * Returns root. 186 * 187 * @return root 188 */ 189 @Override 190 public AbstractNode getRoot() { 191 return root; 192 } 193 194 /** 195 * Returns attribute axis iterator. 196 * 197 * @param predicate filter for the iterator, may be null 198 * @return iterator over the {@code @text} attribute node, or an empty iterator 199 */ 200 @Override 201 public SequenceIterator iterateAttributeAxis(NodePredicate predicate) { 202 return Navigator.filteredSingleton(getAttributeNode(), predicate); 203 } 204 205 /** 206 * Returns child axis iterator. 207 * 208 * @param predicate filter for the iterator, may be null 209 * @return iterator over the children 210 */ 211 @Override 212 public SequenceIterator iterateChildAxis(NodePredicate predicate) { 213 final SequenceIterator result; 214 if (hasChildNodes()) { 215 result = filter(new ArrayIterator.OfNodes<>( 216 getChildren().toArray(EMPTY_ABSTRACT_NODE_ARRAY)), predicate); 217 } 218 else { 219 result = EmptyIterator.getInstance(); 220 } 221 return result; 222 } 223 224 /** 225 * Returns descendant axis iterator. 226 * 227 * @param predicate filter for the iterator, may be null 228 * @return iterator over the descendants 229 */ 230 @Override 231 public SequenceIterator iterateDescendantAxis(NodePredicate predicate) { 232 final SequenceIterator result; 233 if (hasChildNodes()) { 234 result = filter( 235 new DescendantIterator(this, DescendantIterator.StartWith.CHILDREN), predicate); 236 } 237 else { 238 result = EmptyIterator.getInstance(); 239 } 240 return result; 241 } 242 243 /** 244 * Returns descendant-or-self axis iterator. 245 * 246 * @param predicate filter for the iterator, may be null 247 * @return iterator over the current node and its descendants 248 */ 249 @Override 250 public SequenceIterator iterateDescendantOrSelfAxis(NodePredicate predicate) { 251 return filter( 252 new DescendantIterator(this, DescendantIterator.StartWith.CURRENT_NODE), predicate); 253 } 254 255 /** 256 * Returns following-sibling axis iterator. 257 * 258 * @param predicate filter for the iterator, may be null 259 * @return iterator over the following siblings 260 */ 261 @Override 262 public SequenceIterator iterateFollowingSiblingAxis(NodePredicate predicate) { 263 return filter(getFollowingSiblingsIterator(), predicate); 264 } 265 266 /** 267 * Returns preceding-sibling axis iterator. 268 * 269 * @param predicate filter for the iterator, may be null 270 * @return iterator over the preceding siblings 271 */ 272 @Override 273 public SequenceIterator iteratePrecedingSiblingAxis(NodePredicate predicate) { 274 return filter(getPrecedingSiblingsIterator(), predicate); 275 } 276 277 /** 278 * Returns following axis iterator. 279 * 280 * @param predicate filter for the iterator, may be null 281 * @return iterator over the following nodes 282 */ 283 @Override 284 public SequenceIterator iterateFollowingAxis(NodePredicate predicate) { 285 return filter(new FollowingIterator(this), predicate); 286 } 287 288 /** 289 * Returns preceding axis iterator. 290 * 291 * @param predicate filter for the iterator, may be null 292 * @return iterator over the preceding nodes 293 */ 294 @Override 295 public SequenceIterator iteratePrecedingAxis(NodePredicate predicate) { 296 return filter(new PrecedingIterator(this), predicate); 297 } 298 299 /** 300 * Returns preceding sibling axis iterator. 301 * 302 * @return iterator 303 */ 304 private SequenceIterator getPrecedingSiblingsIterator() { 305 final SequenceIterator result; 306 if (indexAmongSiblings == 0) { 307 result = EmptyIterator.getInstance(); 308 } 309 else { 310 result = new ReverseListIterator(getPrecedingSiblings()); 311 } 312 return result; 313 } 314 315 /** 316 * Returns following sibling axis iterator. 317 * 318 * @return iterator 319 */ 320 private SequenceIterator getFollowingSiblingsIterator() { 321 final SequenceIterator result; 322 if (indexAmongSiblings == parent.getChildren().size() - 1) { 323 result = EmptyIterator.getInstance(); 324 } 325 else { 326 result = new ArrayIterator.OfNodes<>( 327 getFollowingSiblings().toArray(EMPTY_ABSTRACT_NODE_ARRAY)); 328 } 329 return result; 330 } 331 332 /** 333 * Returns following siblings of the current node. 334 * 335 * @return siblings 336 */ 337 private List<AbstractNode> getFollowingSiblings() { 338 final List<AbstractNode> siblings = parent.getChildren(); 339 return siblings.subList(indexAmongSiblings + 1, siblings.size()); 340 } 341 342 /** 343 * Returns preceding siblings of the current node. 344 * 345 * @return siblings 346 */ 347 private List<AbstractNode> getPrecedingSiblings() { 348 final List<AbstractNode> siblings = parent.getChildren(); 349 return Collections.unmodifiableList(siblings.subList(0, indexAmongSiblings)); 350 } 351 352 /** 353 * Checks if token type supports {@code @text} attribute, 354 * extracts its value, creates {@code AttributeNode} object and returns it. 355 * Value can be accessed using {@code @text} attribute. 356 * 357 * @return attribute node if possible, otherwise the {@code null} value 358 */ 359 @SuppressWarnings("ReferenceEquality") 360 private AttributeNode getAttributeNode() { 361 if (attributeNode == ATTRIBUTE_NODE_UNINITIALIZED) { 362 attributeNode = createAttributeNode(); 363 } 364 return attributeNode; 365 } 366 367 /** 368 * Namespace axis is not supported for element nodes. 369 * 370 * @param predicate filter for the iterator, may be null 371 * @return never 372 */ 373 @Override 374 public SequenceIterator iterateNamespaceAxis(NodePredicate predicate) { 375 throw new UnsupportedOperationException("Operation is not supported"); 376 } 377 378}