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