View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 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.api.TokenTypes;
24  import com.puppycrawl.tools.checkstyle.utils.TokenUtils;
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   * @author Timur Tibeyev
38   */
39  public class ElementNode extends AbstractNode {
40      /** String literal for text attribute. */
41      private static final String TEXT_ATTRIBUTE_NAME = "text";
42  
43      /** The root node. */
44      private final AbstractNode root;
45  
46      /** The parent of the current node. */
47      private final AbstractNode parent;
48  
49      /** The ast node. */
50      private final DetailAST detailAst;
51  
52      /** Represents text of the DetailAST. */
53      private final String text;
54  
55      /** The attributes. */
56      private AbstractNode[] attributes;
57  
58      /** Represents value of TokenTypes#IDENT. */
59      private String ident;
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          this.parent = parent;
70          this.root = root;
71          this.detailAst = detailAst;
72          setIdent();
73          createChildren();
74          text = TokenUtils.getTokenName(detailAst.getType());
75      }
76  
77      /**
78       * Iterates children of the current node and
79       * recursively creates new Xpath-nodes.
80       */
81      private void createChildren() {
82          DetailAST currentChild = detailAst.getFirstChild();
83          while (currentChild != null) {
84              if (currentChild.getType() != TokenTypes.IDENT) {
85                  final ElementNode child = new ElementNode(root, this, currentChild);
86                  addChild(child);
87              }
88              currentChild = currentChild.getNextSibling();
89          }
90      }
91  
92      /**
93       * Returns attribute value. Throws {@code UnsupportedOperationException} in case,
94       * when name of the attribute is not equal to 'text'.
95       * @param namespace namespace
96       * @param localPart actual name of the attribute
97       * @return attribute value
98       */
99      @Override
100     public String getAttributeValue(String namespace, String localPart) {
101         if (TEXT_ATTRIBUTE_NAME.equals(localPart)) {
102             return ident;
103         }
104         else {
105             throw throwUnsupportedOperationException();
106         }
107     }
108 
109     /**
110      * Returns local part.
111      * @return local part
112      */
113     // -@cs[SimpleAccessorNameNotation] Overrides method from the base class.
114     // Issue: https://github.com/sevntu-checkstyle/sevntu.checkstyle/issues/166
115     @Override
116     public String getLocalPart() {
117         return text;
118     }
119 
120     /**
121      * Returns type of the node.
122      * @return node kind
123      */
124     @Override
125     public int getNodeKind() {
126         return Type.ELEMENT;
127     }
128 
129     /**
130      * Returns parent.
131      * @return parent
132      */
133     @Override
134     public NodeInfo getParent() {
135         return parent;
136     }
137 
138     /**
139      * Returns root.
140      * @return root
141      */
142     @Override
143     public NodeInfo getRoot() {
144         return root;
145     }
146 
147     /**
148      * Returns string value.
149      * @return string value
150      */
151     // -@cs[SimpleAccessorNameNotation] Overrides method from the base class.
152     // Issue: https://github.com/sevntu-checkstyle/sevntu.checkstyle/issues/166
153     @Override
154     public String getStringValue() {
155         return text;
156     }
157 
158     /**
159      * Determines axis iteration algorithm. Throws {@code UnsupportedOperationException} in case,
160      * when there is no axis iterator for given axisNumber.
161      *
162      * @param axisNumber element from {@code AxisInfo}
163      * @return {@code AxisIterator} object
164      */
165     @Override
166     public AxisIterator iterateAxis(byte axisNumber) {
167         final AxisIterator result;
168         switch (axisNumber) {
169             case AxisInfo.ANCESTOR:
170                 result = new Navigator.AncestorEnumeration(this, false);
171                 break;
172             case AxisInfo.ANCESTOR_OR_SELF:
173                 result = new Navigator.AncestorEnumeration(this, true);
174                 break;
175             case AxisInfo.ATTRIBUTE:
176                 if (attributes == null) {
177                     result = EmptyIterator.OfNodes.THE_INSTANCE;
178                 }
179                 else {
180                     result = new ArrayIterator.OfNodes(attributes);
181                 }
182                 break;
183             case AxisInfo.CHILD:
184                 if (hasChildNodes()) {
185                     result = new ArrayIterator.OfNodes(
186                             getChildren().toArray(new AbstractNode[getChildren().size()]));
187                 }
188                 else {
189                     result = EmptyIterator.OfNodes.THE_INSTANCE;
190                 }
191                 break;
192             case AxisInfo.DESCENDANT:
193                 if (hasChildNodes()) {
194                     result = new Navigator.DescendantEnumeration(this, false, true);
195                 }
196                 else {
197                     result = EmptyIterator.OfNodes.THE_INSTANCE;
198                 }
199                 break;
200             case AxisInfo.DESCENDANT_OR_SELF:
201                 result = new Navigator.DescendantEnumeration(this, true, true);
202                 break;
203             case AxisInfo.PARENT:
204                 result = SingleNodeIterator.makeIterator(parent);
205                 break;
206             case AxisInfo.SELF:
207                 result = SingleNodeIterator.makeIterator(this);
208                 break;
209             default:
210                 throw throwUnsupportedOperationException();
211         }
212         return result;
213     }
214 
215     /**
216      * Returns line number.
217      * @return line number
218      */
219     @Override
220     public int getLineNumber() {
221         return detailAst.getLineNo();
222     }
223 
224     /**
225      * Returns column number.
226      * @return column number
227      */
228     @Override
229     public int getColumnNumber() {
230         return detailAst.getColumnNo();
231     }
232 
233     /**
234      * Getter method for token type.
235      * @return token type
236      */
237     @Override
238     public int getTokenType() {
239         return detailAst.getType();
240     }
241 
242     /**
243      * Returns underlying node.
244      * @return underlying node
245      */
246     // -@cs[SimpleAccessorNameNotation] Overrides method from the base class.
247     // Issue: https://github.com/sevntu-checkstyle/sevntu.checkstyle/issues/166
248     @Override
249     public DetailAST getUnderlyingNode() {
250         return detailAst;
251     }
252 
253     /**
254      * Finds child element with {@link TokenTypes#IDENT}, extracts its value and stores it.
255      * Value can be accessed using {@code @text} attribute. Now {@code @text} attribute is only
256      * supported attribute.
257      */
258     private void setIdent() {
259         final DetailAST identAst = detailAst.findFirstToken(TokenTypes.IDENT);
260         if (identAst != null) {
261             ident = identAst.getText();
262             attributes = new AbstractNode[1];
263             attributes[0] = new AttributeNode(TEXT_ATTRIBUTE_NAME, ident);
264         }
265     }
266 
267     /**
268      * Returns UnsupportedOperationException exception.
269      * @return UnsupportedOperationException exception
270      */
271     private static UnsupportedOperationException throwUnsupportedOperationException() {
272         return new UnsupportedOperationException("Operation is not supported");
273     }
274 }