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