1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.xpath;
21  
22  import com.puppycrawl.tools.checkstyle.api.DetailAST;
23  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
24  import com.puppycrawl.tools.checkstyle.utils.XpathUtil;
25  import net.sf.saxon.om.AxisInfo;
26  import net.sf.saxon.om.NodeInfo;
27  import net.sf.saxon.tree.iter.ArrayIterator;
28  import net.sf.saxon.tree.iter.AxisIterator;
29  import net.sf.saxon.tree.iter.EmptyIterator;
30  import net.sf.saxon.tree.iter.SingleNodeIterator;
31  import net.sf.saxon.tree.util.Navigator;
32  import net.sf.saxon.type.Type;
33  
34  /**
35   * Represents element node of Xpath-tree.
36   *
37   */
38  public class ElementNode extends AbstractNode {
39  
40      /** String literal for text attribute. */
41      private static final String TEXT_ATTRIBUTE_NAME = "text";
42  
43      /** Constant for optimization. */
44      private static final AbstractNode[] EMPTY_ABSTRACT_NODE_ARRAY = new AbstractNode[0];
45  
46      /** The root node. */
47      private final AbstractNode root;
48  
49      /** The parent of the current node. */
50      private final AbstractNode parent;
51  
52      /** The ast node. */
53      private final DetailAST detailAst;
54  
55      /** Represents text of the DetailAST. */
56      private final String text;
57  
58      /** The text attribute node. */
59      private AttributeNode attributeNode;
60  
61      /**
62       * Creates a new {@code ElementNode} instance.
63       *
64       * @param root {@code Node} root of the tree
65       * @param parent {@code Node} parent of the current node
66       * @param detailAst reference to {@code DetailAST}
67       */
68      public ElementNode(AbstractNode root, AbstractNode parent, DetailAST detailAst) {
69          super(root.getTreeInfo());
70          this.parent = parent;
71          this.root = root;
72          this.detailAst = detailAst;
73          text = TokenUtil.getTokenName(detailAst.getType());
74          createTextAttribute();
75          createChildren();
76      }
77  
78      /**
79       * Iterates children of the current node and
80       * recursively creates new Xpath-nodes.
81       */
82      private void createChildren() {
83          DetailAST currentChild = detailAst.getFirstChild();
84          while (currentChild != null) {
85              final AbstractNode child = new ElementNode(root, this, currentChild);
86              addChild(child);
87              currentChild = currentChild.getNextSibling();
88          }
89      }
90  
91      /**
92       * Returns attribute value. Throws {@code UnsupportedOperationException} in case,
93       * when name of the attribute is not equal to 'text'.
94       * @param namespace namespace
95       * @param localPart actual name of the attribute
96       * @return attribute value
97       */
98      @Override
99      public String getAttributeValue(String namespace, String localPart) {
100         final String result;
101         if (TEXT_ATTRIBUTE_NAME.equals(localPart)) {
102             if (attributeNode == null) {
103                 result = null;
104             }
105             else {
106                 result = attributeNode.getStringValue();
107             }
108         }
109         else {
110             result = null;
111         }
112         return result;
113     }
114 
115     /**
116      * Returns local part.
117      * @return local part
118      */
119     // -@cs[SimpleAccessorNameNotation] Overrides method from the base class.
120     // Issue: https://github.com/sevntu-checkstyle/sevntu.checkstyle/issues/166
121     @Override
122     public String getLocalPart() {
123         return text;
124     }
125 
126     /**
127      * Returns type of the node.
128      * @return node kind
129      */
130     @Override
131     public int getNodeKind() {
132         return Type.ELEMENT;
133     }
134 
135     /**
136      * Returns parent.
137      * @return parent
138      */
139     @Override
140     public NodeInfo getParent() {
141         return parent;
142     }
143 
144     /**
145      * Returns root.
146      * @return root
147      */
148     @Override
149     public NodeInfo getRoot() {
150         return root;
151     }
152 
153     /**
154      * Returns string value.
155      * @return string value
156      */
157     // -@cs[SimpleAccessorNameNotation] Overrides method from the base class.
158     // Issue: https://github.com/sevntu-checkstyle/sevntu.checkstyle/issues/166
159     @Override
160     public String getStringValue() {
161         return text;
162     }
163 
164     /**
165      * Determines axis iteration algorithm. Throws {@code UnsupportedOperationException} in case,
166      * when there is no axis iterator for given axisNumber.
167      *
168      * @param axisNumber element from {@code AxisInfo}
169      * @return {@code AxisIterator} object
170      */
171     @Override
172     public AxisIterator iterateAxis(byte axisNumber) {
173         final AxisIterator result;
174         switch (axisNumber) {
175             case AxisInfo.ANCESTOR:
176                 try (AxisIterator iterator = new Navigator.AncestorEnumeration(this, false)) {
177                     result = iterator;
178                 }
179                 break;
180             case AxisInfo.ANCESTOR_OR_SELF:
181                 try (AxisIterator iterator = new Navigator.AncestorEnumeration(this, true)) {
182                     result = iterator;
183                 }
184                 break;
185             case AxisInfo.ATTRIBUTE:
186                 try (AxisIterator iterator = SingleNodeIterator.makeIterator(attributeNode)) {
187                     result = iterator;
188                 }
189                 break;
190             case AxisInfo.CHILD:
191                 if (hasChildNodes()) {
192                     try (AxisIterator iterator = new ArrayIterator.OfNodes(
193                             getChildren().toArray(EMPTY_ABSTRACT_NODE_ARRAY))) {
194                         result = iterator;
195                     }
196                 }
197                 else {
198                     result = EmptyIterator.OfNodes.THE_INSTANCE;
199                 }
200                 break;
201             case AxisInfo.DESCENDANT:
202                 if (hasChildNodes()) {
203                     try (AxisIterator iterator =
204                                  new Navigator.DescendantEnumeration(this, false, true)) {
205                         result = iterator;
206                     }
207                 }
208                 else {
209                     result = EmptyIterator.OfNodes.THE_INSTANCE;
210                 }
211                 break;
212             case AxisInfo.DESCENDANT_OR_SELF:
213                 try (AxisIterator iterator =
214                              new Navigator.DescendantEnumeration(this, true, true)) {
215                     result = iterator;
216                 }
217                 break;
218             case AxisInfo.PARENT:
219                 try (AxisIterator iterator = SingleNodeIterator.makeIterator(parent)) {
220                     result = iterator;
221                 }
222                 break;
223             case AxisInfo.SELF:
224                 try (AxisIterator iterator = SingleNodeIterator.makeIterator(this)) {
225                     result = iterator;
226                 }
227                 break;
228             default:
229                 throw throwUnsupportedOperationException();
230         }
231         return result;
232     }
233 
234     /**
235      * Returns line number.
236      * @return line number
237      */
238     @Override
239     public int getLineNumber() {
240         return detailAst.getLineNo();
241     }
242 
243     /**
244      * Returns column number.
245      * @return column number
246      */
247     @Override
248     public int getColumnNumber() {
249         return detailAst.getColumnNo();
250     }
251 
252     /**
253      * Getter method for token type.
254      * @return token type
255      */
256     @Override
257     public int getTokenType() {
258         return detailAst.getType();
259     }
260 
261     /**
262      * Returns underlying node.
263      * @return underlying node
264      */
265     // -@cs[SimpleAccessorNameNotation] Overrides method from the base class.
266     // Issue: https://github.com/sevntu-checkstyle/sevntu.checkstyle/issues/166
267     @Override
268     public DetailAST getUnderlyingNode() {
269         return detailAst;
270     }
271 
272     /**
273      * Checks if token type supports {@code @text} attribute,
274      * extracts its value, creates {@code AttributeNode} object and returns it.
275      * Value can be accessed using {@code @text} attribute.
276      */
277     private void createTextAttribute() {
278         AttributeNode attribute = null;
279         if (XpathUtil.supportsTextAttribute(detailAst)) {
280             attribute = new AttributeNode(TEXT_ATTRIBUTE_NAME,
281                     XpathUtil.getTextAttributeValue(detailAst));
282         }
283         attributeNode = attribute;
284     }
285 
286     /**
287      * Returns UnsupportedOperationException exception.
288      * @return UnsupportedOperationException exception
289      */
290     private static UnsupportedOperationException throwUnsupportedOperationException() {
291         return new UnsupportedOperationException("Operation is not supported");
292     }
293 
294 }