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 }