001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2018 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 com.puppycrawl.tools.checkstyle.api.DetailAST;
023import com.puppycrawl.tools.checkstyle.api.TokenTypes;
024import com.puppycrawl.tools.checkstyle.utils.TokenUtils;
025import net.sf.saxon.om.AxisInfo;
026import net.sf.saxon.om.NodeInfo;
027import net.sf.saxon.tree.iter.ArrayIterator;
028import net.sf.saxon.tree.iter.AxisIterator;
029import net.sf.saxon.tree.iter.EmptyIterator;
030import net.sf.saxon.tree.iter.SingleNodeIterator;
031import net.sf.saxon.tree.util.Navigator;
032import net.sf.saxon.type.Type;
033
034/**
035 * Represents element node of Xpath-tree.
036 *
037 */
038public class ElementNode extends AbstractNode {
039
040    /** String literal for text attribute. */
041    private static final String TEXT_ATTRIBUTE_NAME = "text";
042
043    /** The root node. */
044    private final AbstractNode root;
045
046    /** The parent of the current node. */
047    private final AbstractNode parent;
048
049    /** The ast node. */
050    private final DetailAST detailAst;
051
052    /** Represents text of the DetailAST. */
053    private final String text;
054
055    /** The attributes. */
056    private AbstractNode[] attributes;
057
058    /** Represents value of TokenTypes#IDENT. */
059    private String ident;
060
061    /**
062     * Creates a new {@code ElementNode} instance.
063     *
064     * @param root {@code Node} root of the tree
065     * @param parent {@code Node} parent of the current node
066     * @param detailAst reference to {@code DetailAST}
067     */
068    public ElementNode(AbstractNode root, AbstractNode parent, DetailAST detailAst) {
069        super(root.getTreeInfo());
070        this.parent = parent;
071        this.root = root;
072        this.detailAst = detailAst;
073        setIdent();
074        createChildren();
075        text = TokenUtils.getTokenName(detailAst.getType());
076    }
077
078    /**
079     * Iterates children of the current node and
080     * recursively creates new Xpath-nodes.
081     */
082    private void createChildren() {
083        DetailAST currentChild = detailAst.getFirstChild();
084        while (currentChild != null) {
085            final AbstractNode child = new ElementNode(root, this, currentChild);
086            addChild(child);
087            currentChild = currentChild.getNextSibling();
088        }
089    }
090
091    /**
092     * Returns attribute value. Throws {@code UnsupportedOperationException} in case,
093     * when name of the attribute is not equal to 'text'.
094     * @param namespace namespace
095     * @param localPart actual name of the attribute
096     * @return attribute value
097     */
098    @Override
099    public String getAttributeValue(String namespace, String localPart) {
100        if (TEXT_ATTRIBUTE_NAME.equals(localPart)) {
101            return ident;
102        }
103        else {
104            throw throwUnsupportedOperationException();
105        }
106    }
107
108    /**
109     * Returns local part.
110     * @return local part
111     */
112    // -@cs[SimpleAccessorNameNotation] Overrides method from the base class.
113    // Issue: https://github.com/sevntu-checkstyle/sevntu.checkstyle/issues/166
114    @Override
115    public String getLocalPart() {
116        return text;
117    }
118
119    /**
120     * Returns type of the node.
121     * @return node kind
122     */
123    @Override
124    public int getNodeKind() {
125        return Type.ELEMENT;
126    }
127
128    /**
129     * Returns parent.
130     * @return parent
131     */
132    @Override
133    public NodeInfo getParent() {
134        return parent;
135    }
136
137    /**
138     * Returns root.
139     * @return root
140     */
141    @Override
142    public NodeInfo getRoot() {
143        return root;
144    }
145
146    /**
147     * Returns string value.
148     * @return string value
149     */
150    // -@cs[SimpleAccessorNameNotation] Overrides method from the base class.
151    // Issue: https://github.com/sevntu-checkstyle/sevntu.checkstyle/issues/166
152    @Override
153    public String getStringValue() {
154        return text;
155    }
156
157    /**
158     * Determines axis iteration algorithm. Throws {@code UnsupportedOperationException} in case,
159     * when there is no axis iterator for given axisNumber.
160     *
161     * @param axisNumber element from {@code AxisInfo}
162     * @return {@code AxisIterator} object
163     */
164    @Override
165    public AxisIterator iterateAxis(byte axisNumber) {
166        final AxisIterator result;
167        switch (axisNumber) {
168            case AxisInfo.ANCESTOR:
169                result = new Navigator.AncestorEnumeration(this, false);
170                break;
171            case AxisInfo.ANCESTOR_OR_SELF:
172                result = new Navigator.AncestorEnumeration(this, true);
173                break;
174            case AxisInfo.ATTRIBUTE:
175                if (attributes == null) {
176                    result = EmptyIterator.OfNodes.THE_INSTANCE;
177                }
178                else {
179                    result = new ArrayIterator.OfNodes(attributes);
180                }
181                break;
182            case AxisInfo.CHILD:
183                if (hasChildNodes()) {
184                    result = new ArrayIterator.OfNodes(
185                            getChildren().toArray(new AbstractNode[getChildren().size()]));
186                }
187                else {
188                    result = EmptyIterator.OfNodes.THE_INSTANCE;
189                }
190                break;
191            case AxisInfo.DESCENDANT:
192                if (hasChildNodes()) {
193                    result = new Navigator.DescendantEnumeration(this, false, true);
194                }
195                else {
196                    result = EmptyIterator.OfNodes.THE_INSTANCE;
197                }
198                break;
199            case AxisInfo.DESCENDANT_OR_SELF:
200                result = new Navigator.DescendantEnumeration(this, true, true);
201                break;
202            case AxisInfo.PARENT:
203                result = SingleNodeIterator.makeIterator(parent);
204                break;
205            case AxisInfo.SELF:
206                result = SingleNodeIterator.makeIterator(this);
207                break;
208            default:
209                throw throwUnsupportedOperationException();
210        }
211        return result;
212    }
213
214    /**
215     * Returns line number.
216     * @return line number
217     */
218    @Override
219    public int getLineNumber() {
220        return detailAst.getLineNo();
221    }
222
223    /**
224     * Returns column number.
225     * @return column number
226     */
227    @Override
228    public int getColumnNumber() {
229        return detailAst.getColumnNo();
230    }
231
232    /**
233     * Getter method for token type.
234     * @return token type
235     */
236    @Override
237    public int getTokenType() {
238        return detailAst.getType();
239    }
240
241    /**
242     * Returns underlying node.
243     * @return underlying node
244     */
245    // -@cs[SimpleAccessorNameNotation] Overrides method from the base class.
246    // Issue: https://github.com/sevntu-checkstyle/sevntu.checkstyle/issues/166
247    @Override
248    public DetailAST getUnderlyingNode() {
249        return detailAst;
250    }
251
252    /**
253     * Finds child element with {@link TokenTypes#IDENT}, extracts its value and stores it.
254     * Value can be accessed using {@code @text} attribute. Now {@code @text} attribute is only
255     * supported attribute.
256     */
257    private void setIdent() {
258        final DetailAST identAst = detailAst.findFirstToken(TokenTypes.IDENT);
259        if (identAst != null) {
260            ident = identAst.getText();
261            attributes = new AbstractNode[1];
262            attributes[0] = new AttributeNode(TEXT_ATTRIBUTE_NAME, ident);
263        }
264    }
265
266    /**
267     * Returns UnsupportedOperationException exception.
268     * @return UnsupportedOperationException exception
269     */
270    private static UnsupportedOperationException throwUnsupportedOperationException() {
271        return new UnsupportedOperationException("Operation is not supported");
272    }
273
274}