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      /**
43       * The serial version UID.
44       */
45      private static final long serialVersionUID = -1087475639680007713L;
46  
47      /** The node handler. */
48      private final NodeHandler<T> handler;
49  
50      /** Stores the associated node. */
51      private final T node;
52  
53      /**
54       * Creates a new instance of {@code ConfigurationNodePointer} and initializes it with its parent pointer.
55       *
56       * @param parent the parent pointer
57       * @param node the associated node
58       * @param handler the {@code NodeHandler}
59       */
60      public ConfigurationNodePointer(final ConfigurationNodePointer<T> parent, final T node, final NodeHandler<T> handler) {
61          super(parent);
62          this.node = node;
63          this.handler = handler;
64      }
65  
66      /**
67       * Creates a new instance of {@code ConfigurationNodePointer} pointing to the specified node.
68       *
69       * @param node the wrapped node
70       * @param locale the locale
71       * @param handler the {@code NodeHandler}
72       */
73      public ConfigurationNodePointer(final T node, final Locale locale, final NodeHandler<T> handler) {
74          super(null, locale);
75          this.node = node;
76          this.handler = handler;
77      }
78  
79      /**
80       * Returns an iterator for the attributes that match the given name.
81       *
82       * @param qName the attribute name
83       * @return the iterator for the attributes
84       */
85      @Override
86      public NodeIterator attributeIterator(final QName qName) {
87          return new ConfigurationNodeIteratorAttribute<>(this, qName);
88      }
89  
90      /**
91       * Casts the given child pointer to a node pointer of this type. This is a bit dangerous. However, in a typical setup,
92       * child node pointers can only be created by this instance which ensures that they are of the correct type. Therefore,
93       * this cast is safe.
94       *
95       * @param p the {@code NodePointer} to cast
96       * @return the resulting {@code ConfigurationNodePointer}
97       */
98      private ConfigurationNodePointer<T> castPointer(final NodePointer p) {
99          @SuppressWarnings("unchecked") // see method comment
100         final ConfigurationNodePointer<T> result = (ConfigurationNodePointer<T>) p;
101         return result;
102     }
103 
104     /**
105      * Returns an iterator for the children of this pointer that match the given test object.
106      *
107      * @param test the test object
108      * @param reverse the reverse flag
109      * @param startWith the start value of the iteration
110      */
111     @Override
112     public NodeIterator childIterator(final NodeTest test, final boolean reverse, final NodePointer startWith) {
113         return new ConfigurationNodeIteratorChildren<>(this, test, reverse, castPointer(startWith));
114     }
115 
116     /**
117      * Compares two child node pointers.
118      *
119      * @param pointer1 one pointer
120      * @param pointer2 another pointer
121      * @return a flag, which pointer should be sorted first
122      */
123     @Override
124     public int compareChildNodePointers(final NodePointer pointer1, final NodePointer pointer2) {
125         final Object node1 = pointer1.getBaseValue();
126         final Object node2 = pointer2.getBaseValue();
127 
128         // sort based on the occurrence in the sub node list
129         for (final T child : getNodeHandler().getChildren(node)) {
130             if (child == node1) {
131                 return -1;
132             }
133             if (child == node2) {
134                 return 1;
135             }
136         }
137         return 0; // should not happen
138     }
139 
140     /**
141      * Gets this node's base value. This is the associated configuration node.
142      *
143      * @return the base value
144      */
145     @Override
146     public Object getBaseValue() {
147         return node;
148     }
149 
150     /**
151      * Gets the wrapped configuration node.
152      *
153      * @return the wrapped node
154      */
155     public T getConfigurationNode() {
156         return node;
157     }
158 
159     /**
160      * Gets the immediate node. This is the associated configuration node.
161      *
162      * @return the immediate node
163      */
164     @Override
165     public Object getImmediateNode() {
166         return node;
167     }
168 
169     /**
170      * Gets this node's length. This is always 1.
171      *
172      * @return the node's length
173      */
174     @Override
175     public int getLength() {
176         return 1;
177     }
178 
179     /**
180      * Gets this node's name.
181      *
182      * @return the name
183      */
184     @Override
185     public QName getName() {
186         return new QName(null, getNodeHandler().nodeName(node));
187     }
188 
189     /**
190      * Gets the {@code NodeHandler} used by this instance.
191      *
192      * @return the {@code NodeHandler}
193      */
194     public NodeHandler<T> getNodeHandler() {
195         return handler;
196     }
197 
198     /**
199      * Gets the value of this node.
200      *
201      * @return the represented node's value
202      */
203     @Override
204     public Object getValue() {
205         return getNodeHandler().getValue(node);
206     }
207 
208     /**
209      * Checks whether this node pointer refers to an attribute node. This is not the case.
210      *
211      * @return the attribute flag
212      */
213     @Override
214     public boolean isAttribute() {
215         return false;
216     }
217 
218     /**
219      * Returns a flag if this node is a collection. This is not the case.
220      *
221      * @return the collection flag
222      */
223     @Override
224     public boolean isCollection() {
225         return false;
226     }
227 
228     /**
229      * Returns a flag whether this node is a leaf. This is the case if there are no child nodes.
230      *
231      * @return a flag if this node is a leaf
232      */
233     @Override
234     public boolean isLeaf() {
235         return getNodeHandler().getChildrenCount(node, null) < 1;
236     }
237 
238     /**
239      * Sets the value of this node. This is not supported, so always an exception is thrown.
240      *
241      * @param value the new value
242      */
243     @Override
244     public void setValue(final Object value) {
245         throw new UnsupportedOperationException("Node value cannot be set.");
246     }
247 
248     /**
249      * Tests if this node matches the given test. Configuration nodes are text nodes, too because they can contain a value.
250      *
251      * @param test the test object
252      * @return a flag if this node corresponds to the test
253      */
254     @Override
255     public boolean testNode(final NodeTest test) {
256         if (test instanceof NodeTypeTest && ((NodeTypeTest) test).getNodeType() == Compiler.NODE_TYPE_TEXT) {
257             return true;
258         }
259         return super.testNode(test);
260     }
261 }