001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 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 net.sf.saxon.Configuration;
025import net.sf.saxon.om.AxisInfo;
026import net.sf.saxon.om.GenericTreeInfo;
027import net.sf.saxon.om.NodeInfo;
028import net.sf.saxon.tree.iter.ArrayIterator;
029import net.sf.saxon.tree.iter.AxisIterator;
030import net.sf.saxon.tree.iter.EmptyIterator;
031import net.sf.saxon.tree.iter.SingleNodeIterator;
032import net.sf.saxon.tree.util.Navigator;
033import net.sf.saxon.type.Type;
034
035/**
036 * Represents root node of Xpath-tree.
037 *
038 */
039public class RootNode extends AbstractNode {
040
041    /** Name of the root element. */
042    private static final String ROOT_NAME = "ROOT";
043
044    /** Constant for optimization. */
045    private static final AbstractNode[] EMPTY_ABSTRACT_NODE_ARRAY = new AbstractNode[0];
046
047    /** The ast node. */
048    private final DetailAST detailAst;
049
050    /**
051     * Creates a new {@code RootNode} instance.
052     *
053     * @param detailAst reference to {@code DetailAST}
054     */
055    public RootNode(DetailAST detailAst) {
056        super(new GenericTreeInfo(Configuration.newConfiguration()));
057        this.detailAst = detailAst;
058
059        createChildren();
060    }
061
062    /**
063     * Iterates siblings of the current node and
064     * recursively creates new Xpath-nodes.
065     */
066    private void createChildren() {
067        DetailAST currentChild = detailAst;
068        while (currentChild != null) {
069            final ElementNode child = new ElementNode(this, this, currentChild);
070            addChild(child);
071            currentChild = currentChild.getNextSibling();
072        }
073    }
074
075    /**
076     * Returns attribute value. Throws {@code UnsupportedOperationException} because root node
077     * has no attributes.
078     * @param namespace namespace
079     * @param localPart actual name of the attribute
080     * @return attribute value
081     */
082    @Override
083    public String getAttributeValue(String namespace, String localPart) {
084        throw throwUnsupportedOperationException();
085    }
086
087    /**
088     * Returns local part.
089     * @return local part
090     */
091    @Override
092    public String getLocalPart() {
093        return ROOT_NAME;
094    }
095
096    /**
097     * Returns type of the node.
098     * @return node kind
099     */
100    @Override
101    public int getNodeKind() {
102        return Type.DOCUMENT;
103    }
104
105    /**
106     * Returns parent.
107     * @return parent
108     */
109    @Override
110    public NodeInfo getParent() {
111        return null;
112    }
113
114    /**
115     * Returns root of the tree.
116     * @return root of the tree
117     */
118    @Override
119    public NodeInfo getRoot() {
120        return this;
121    }
122
123    /**
124     * Returns string value.
125     * @return string value
126     */
127    @Override
128    public String getStringValue() {
129        return ROOT_NAME;
130    }
131
132    /**
133     * Determines axis iteration algorithm. Throws {@code UnsupportedOperationException} in case,
134     * when there is no axis iterator for given axisNumber.
135     * @param axisNumber element from {@code AxisInfo}
136     * @return {@code AxisIterator} object
137     */
138    @Override
139    public AxisIterator iterateAxis(byte axisNumber) {
140        final AxisIterator result;
141        switch (axisNumber) {
142            case AxisInfo.ANCESTOR:
143            case AxisInfo.ATTRIBUTE:
144            case AxisInfo.PARENT:
145            case AxisInfo.FOLLOWING:
146            case AxisInfo.FOLLOWING_SIBLING:
147            case AxisInfo.PRECEDING:
148            case AxisInfo.PRECEDING_SIBLING:
149                result = EmptyIterator.OfNodes.THE_INSTANCE;
150                break;
151            case AxisInfo.ANCESTOR_OR_SELF:
152            case AxisInfo.SELF:
153                try (AxisIterator iterator = SingleNodeIterator.makeIterator(this)) {
154                    result = iterator;
155                }
156                break;
157            case AxisInfo.CHILD:
158                if (hasChildNodes()) {
159                    try (AxisIterator iterator = new ArrayIterator.OfNodes(
160                            getChildren().toArray(EMPTY_ABSTRACT_NODE_ARRAY))) {
161                        result = iterator;
162                    }
163                }
164                else {
165                    result = EmptyIterator.OfNodes.THE_INSTANCE;
166                }
167                break;
168            case AxisInfo.DESCENDANT:
169                if (hasChildNodes()) {
170                    try (AxisIterator iterator =
171                                 new Navigator.DescendantEnumeration(this, false, true)) {
172                        result = iterator;
173                    }
174                }
175                else {
176                    result = EmptyIterator.OfNodes.THE_INSTANCE;
177                }
178                break;
179            case AxisInfo.DESCENDANT_OR_SELF:
180                try (AxisIterator iterator =
181                             new Navigator.DescendantEnumeration(this, true, true)) {
182                    result = iterator;
183                }
184                break;
185            default:
186                throw throwUnsupportedOperationException();
187        }
188        return result;
189    }
190
191    /**
192     * Returns line number.
193     * @return line number
194     */
195    @Override
196    public int getLineNumber() {
197        return detailAst.getLineNo();
198    }
199
200    /**
201     * Returns column number.
202     * @return column number
203     */
204    @Override
205    public int getColumnNumber() {
206        return detailAst.getColumnNo();
207    }
208
209    /**
210     * Getter method for token type.
211     * @return token type
212     */
213    @Override
214    public int getTokenType() {
215        return TokenTypes.EOF;
216    }
217
218    /**
219     * Returns underlying node.
220     * @return underlying node
221     */
222    @Override
223    public DetailAST getUnderlyingNode() {
224        return detailAst;
225    }
226
227    /**
228     * Returns UnsupportedOperationException exception.
229     * @return UnsupportedOperationException exception
230     */
231    private static UnsupportedOperationException throwUnsupportedOperationException() {
232        return new UnsupportedOperationException("Operation is not supported");
233    }
234
235}