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 }