View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.configuration2.tree.xpath;
18  
19  import java.util.Locale;
20  
21  import org.apache.commons.configuration2.tree.NodeHandler;
22  import org.apache.commons.jxpath.ri.Compiler;
23  import org.apache.commons.jxpath.ri.QName;
24  import org.apache.commons.jxpath.ri.compiler.NodeTest;
25  import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
26  import org.apache.commons.jxpath.ri.model.NodeIterator;
27  import org.apache.commons.jxpath.ri.model.NodePointer;
28  
29  /**
30   * <p>
31   * A specific {@code NodePointer} implementation for configuration nodes.
32   * </p>
33   * <p>
34   * This is needed for queries using JXPath.
35   * </p>
36   *
37   * @param <T> the type of the nodes this pointer deals with
38   * @since 1.3
39   */
40  final class ConfigurationNodePointer<T> extends NodePointer {
41      /**
42       * The serial version UID.
43       */
44      private static final long serialVersionUID = -1087475639680007713L;
45  
46      /** The node handler. */
47      private final NodeHandler<T> handler;
48  
49      /** Stores the associated node. */
50      private final T node;
51  
52      /**
53       * Creates a new instance of {@code ConfigurationNodePointer} and initializes it with its parent pointer.
54       *
55       * @param parent the parent pointer
56       * @param node the associated node
57       * @param handler the {@code NodeHandler}
58       */
59      public ConfigurationNodePointer(final ConfigurationNodePointer<T> parent, final T node, final NodeHandler<T> handler) {
60          super(parent);
61          this.node = node;
62          this.handler = handler;
63      }
64  
65      /**
66       * Creates a new instance of {@code ConfigurationNodePointer} pointing to the specified node.
67       *
68       * @param node the wrapped node
69       * @param locale the locale
70       * @param handler the {@code NodeHandler}
71       */
72      public ConfigurationNodePointer(final T node, final Locale locale, final NodeHandler<T> handler) {
73          super(null, locale);
74          this.node = node;
75          this.handler = handler;
76      }
77  
78      /**
79       * Returns an iterator for the attributes that match the given name.
80       *
81       * @param qName the attribute name
82       * @return the iterator for the attributes
83       */
84      @Override
85      public NodeIterator attributeIterator(final QName qName) {
86          return new ConfigurationNodeIteratorAttribute<>(this, qName);
87      }
88  
89      /**
90       * Casts the given child pointer to a node pointer of this type. This is a bit dangerous. However, in a typical setup,
91       * child node pointers can only be created by this instance which ensures that they are of the correct type. Therefore,
92       * this cast is safe.
93       *
94       * @param p the {@code NodePointer} to cast
95       * @return the resulting {@code ConfigurationNodePointer}
96       */
97      private ConfigurationNodePointer<T> castPointer(final NodePointer p) {
98          @SuppressWarnings("unchecked") // see method comment
99          final ConfigurationNodePointer<T> result = (ConfigurationNodePointer<T>) p;
100         return result;
101     }
102 
103     /**
104      * Returns an iterator for the children of this pointer that match the given test object.
105      *
106      * @param test the test object
107      * @param reverse the reverse flag
108      * @param startWith the start value of the iteration
109      */
110     @Override
111     public NodeIterator childIterator(final NodeTest test, final boolean reverse, final NodePointer startWith) {
112         return new ConfigurationNodeIteratorChildren<>(this, test, reverse, castPointer(startWith));
113     }
114 
115     /**
116      * Compares two child node pointers.
117      *
118      * @param pointer1 one pointer
119      * @param pointer2 another pointer
120      * @return a flag, which pointer should be sorted first
121      */
122     @Override
123     public int compareChildNodePointers(final NodePointer pointer1, final NodePointer pointer2) {
124         final Object node1 = pointer1.getBaseValue();
125         final Object node2 = pointer2.getBaseValue();
126 
127         // sort based on the occurrence in the sub node list
128         for (final T child : getNodeHandler().getChildren(node)) {
129             if (child == node1) {
130                 return -1;
131             }
132             if (child == node2) {
133                 return 1;
134             }
135         }
136         return 0; // should not happen
137     }
138 
139     /**
140      * Gets this node's base value. This is the associated configuration node.
141      *
142      * @return the base value
143      */
144     @Override
145     public Object getBaseValue() {
146         return node;
147     }
148 
149     /**
150      * Gets the wrapped configuration node.
151      *
152      * @return the wrapped node
153      */
154     public T getConfigurationNode() {
155         return node;
156     }
157 
158     /**
159      * Gets the immediate node. This is the associated configuration node.
160      *
161      * @return the immediate node
162      */
163     @Override
164     public Object getImmediateNode() {
165         return node;
166     }
167 
168     /**
169      * Gets this node's length. This is always 1.
170      *
171      * @return the node's length
172      */
173     @Override
174     public int getLength() {
175         return 1;
176     }
177 
178     /**
179      * Gets this node's name.
180      *
181      * @return the name
182      */
183     @Override
184     public QName getName() {
185         return new QName(null, getNodeHandler().nodeName(node));
186     }
187 
188     /**
189      * Gets the {@code NodeHandler} used by this instance.
190      *
191      * @return the {@code NodeHandler}
192      */
193     public NodeHandler<T> getNodeHandler() {
194         return handler;
195     }
196 
197     /**
198      * Gets the value of this node.
199      *
200      * @return the represented node's value
201      */
202     @Override
203     public Object getValue() {
204         return getNodeHandler().getValue(node);
205     }
206 
207     /**
208      * Checks whether this node pointer refers to an attribute node. This is not the case.
209      *
210      * @return the attribute flag
211      */
212     @Override
213     public boolean isAttribute() {
214         return false;
215     }
216 
217     /**
218      * Returns a flag if this node is a collection. This is not the case.
219      *
220      * @return the collection flag
221      */
222     @Override
223     public boolean isCollection() {
224         return false;
225     }
226 
227     /**
228      * Returns a flag whether this node is a leaf. This is the case if there are no child nodes.
229      *
230      * @return a flag if this node is a leaf
231      */
232     @Override
233     public boolean isLeaf() {
234         return getNodeHandler().getChildrenCount(node, null) < 1;
235     }
236 
237     /**
238      * Sets the value of this node. This is not supported, so always an exception is thrown.
239      *
240      * @param value the new value
241      */
242     @Override
243     public void setValue(final Object value) {
244         throw new UnsupportedOperationException("Node value cannot be set!");
245     }
246 
247     /**
248      * Tests if this node matches the given test. Configuration nodes are text nodes, too because they can contain a value.
249      *
250      * @param test the test object
251      * @return a flag if this node corresponds to the test
252      */
253     @Override
254     public boolean testNode(final NodeTest test) {
255         if (test instanceof NodeTypeTest && ((NodeTypeTest) test).getNodeType() == Compiler.NODE_TYPE_TEXT) {
256             return true;
257         }
258         return super.testNode(test);
259     }
260 }