1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.jxpath.ri.model;
19
20 import org.apache.commons.jxpath.AbstractFactory;
21 import org.apache.commons.jxpath.JXPathAbstractFactoryException;
22 import org.apache.commons.jxpath.JXPathContext;
23 import org.apache.commons.jxpath.JXPathException;
24 import org.apache.commons.jxpath.JXPathIntrospector;
25 import org.apache.commons.jxpath.JXPathInvalidAccessException;
26 import org.apache.commons.jxpath.Variables;
27 import org.apache.commons.jxpath.ri.QName;
28 import org.apache.commons.jxpath.ri.compiler.NodeTest;
29 import org.apache.commons.jxpath.ri.model.beans.NullPointer;
30 import org.apache.commons.jxpath.util.ValueUtils;
31
32
33
34
35 public class VariablePointer extends NodePointer {
36
37 private static final long serialVersionUID = -454731297397189293L;
38
39
40
41
42 private Variables variables;
43
44
45
46
47 private final QName qName;
48
49
50
51
52 private NodePointer valuePointer;
53
54
55
56
57 private boolean actual;
58
59
60
61
62
63
64 public VariablePointer(final QName qName) {
65 super(null);
66 this.qName = qName;
67 actual = false;
68 }
69
70
71
72
73
74
75
76 public VariablePointer(final Variables variables, final QName qName) {
77 super(null);
78 this.variables = variables;
79 this.qName = qName;
80 actual = true;
81 }
82
83 @Override
84 public String asPath() {
85 final StringBuilder buffer = new StringBuilder();
86 buffer.append('$');
87 buffer.append(qName);
88 if (!actual) {
89 if (index != WHOLE_COLLECTION) {
90 buffer.append('[').append(index + 1).append(']');
91 }
92 } else if (index != WHOLE_COLLECTION && (getNode() == null || isCollection())) {
93 buffer.append('[').append(index + 1).append(']');
94 }
95 return buffer.toString();
96 }
97
98 @Override
99 public NodeIterator attributeIterator(final QName qName) {
100 return getValuePointer().attributeIterator(qName);
101 }
102
103 @Override
104 public NodeIterator childIterator(final NodeTest test, final boolean reverse, final NodePointer startWith) {
105 return getValuePointer().childIterator(test, reverse, startWith);
106 }
107
108 @Override
109 public int compareChildNodePointers(final NodePointer pointer1, final NodePointer pointer2) {
110 return pointer1.getIndex() - pointer2.getIndex();
111 }
112
113 @Override
114 public NodePointer createChild(final JXPathContext context, final QName qName, final int index) {
115 final Object collection = createCollection(context, index);
116 if (!isActual() || index != 0 && index != WHOLE_COLLECTION) {
117 final AbstractFactory factory = getAbstractFactory(context);
118 final boolean success = factory.createObject(context, this, collection, getName().toString(), index);
119 if (!success) {
120 throw new JXPathAbstractFactoryException("Factory could not create object path: " + asPath());
121 }
122 final NodePointer cln = (NodePointer) clone();
123 cln.setIndex(index);
124 return cln;
125 }
126 return this;
127 }
128
129 @Override
130 public NodePointer createChild(final JXPathContext context, final QName qName, final int index, final Object value) {
131 final Object collection = createCollection(context, index);
132 ValueUtils.setValue(collection, index, value);
133 final NodePointer cl = (NodePointer) clone();
134 cl.setIndex(index);
135 return cl;
136 }
137
138
139
140
141
142
143
144
145 private Object createCollection(final JXPathContext context, int index) {
146 createPath(context);
147 Object collection = getBaseValue();
148 if (collection == null) {
149 throw new JXPathAbstractFactoryException("Factory did not assign a collection to variable '" + qName + "' for path: " + asPath());
150 }
151 if (index == WHOLE_COLLECTION) {
152 index = 0;
153 } else if (index < 0) {
154 throw new JXPathInvalidAccessException("Index is less than 1: " + asPath());
155 }
156 if (index >= getLength()) {
157 collection = ValueUtils.expandCollection(collection, index + 1);
158 variables.declareVariable(qName.toString(), collection);
159 }
160 return collection;
161 }
162
163 @Override
164 public NodePointer createPath(final JXPathContext context) {
165 if (!actual) {
166 final AbstractFactory factory = getAbstractFactory(context);
167 if (!factory.declareVariable(context, qName.toString())) {
168 throw new JXPathAbstractFactoryException("Factory cannot define variable '" + qName + "' for path: " + asPath());
169 }
170 findVariables(context);
171
172 }
173 return this;
174 }
175
176 @Override
177 public NodePointer createPath(final JXPathContext context, final Object value) {
178 if (actual) {
179 setValue(value);
180 return this;
181 }
182 final NodePointer ptr = createPath(context);
183 ptr.setValue(value);
184 return ptr;
185 }
186
187 @Override
188 public boolean equals(final Object object) {
189 if (object == this) {
190 return true;
191 }
192 if (!(object instanceof VariablePointer)) {
193 return false;
194 }
195 final VariablePointer other = (VariablePointer) object;
196 return variables == other.variables && qName.equals(other.qName) && index == other.index;
197 }
198
199
200
201
202
203
204 protected void findVariables(final JXPathContext context) {
205 valuePointer = null;
206 JXPathContext varCtx = context;
207 while (varCtx != null) {
208 variables = varCtx.getVariables();
209 if (variables.isDeclaredVariable(qName.toString())) {
210 actual = true;
211 break;
212 }
213 varCtx = varCtx.getParentContext();
214 variables = null;
215 }
216 }
217
218 @Override
219 public Object getBaseValue() {
220 if (!actual) {
221 throw new JXPathException("Undefined variable: " + qName);
222 }
223 return variables.getVariable(qName.toString());
224 }
225
226 @Override
227 public Object getImmediateNode() {
228 final Object value = getBaseValue();
229 return index == WHOLE_COLLECTION ? ValueUtils.getValue(value) : ValueUtils.getValue(value, index);
230 }
231
232 @Override
233 public NodePointer getImmediateValuePointer() {
234 if (valuePointer == null) {
235 Object value;
236 if (!actual) {
237 return new NullPointer(this, getName()) {
238
239 private static final long serialVersionUID = 1L;
240
241 @Override
242 public Object getImmediateNode() {
243 throw new JXPathException("Undefined variable: " + qName);
244 }
245 };
246 }
247 value = getImmediateNode();
248 valuePointer = newChildNodePointer(this, null, value);
249 }
250 return valuePointer;
251 }
252
253 @Override
254 public int getLength() {
255 if (actual) {
256 final Object value = getBaseValue();
257 return value == null ? 1 : ValueUtils.getLength(value);
258 }
259 return 0;
260 }
261
262 @Override
263 public QName getName() {
264 return qName;
265 }
266
267 @Override
268 public int hashCode() {
269 return (actual ? System.identityHashCode(variables) : 0) + qName.hashCode() + index;
270 }
271
272 @Override
273 public boolean isActual() {
274 return actual;
275 }
276
277 @Override
278 public boolean isCollection() {
279 final Object value = getBaseValue();
280 return value != null && ValueUtils.isCollection(value);
281 }
282
283 @Override
284 public boolean isContainer() {
285 return true;
286 }
287
288 @Override
289 public boolean isLeaf() {
290 final Object value = getNode();
291 return value == null || JXPathIntrospector.getBeanInfo(value.getClass()).isAtomic();
292 }
293
294 @Override
295 public NodeIterator namespaceIterator() {
296 return getValuePointer().namespaceIterator();
297 }
298
299 @Override
300 public NodePointer namespacePointer(final String name) {
301 return getValuePointer().namespacePointer(name);
302 }
303
304 @Override
305 public void remove() {
306 if (actual) {
307 if (index == WHOLE_COLLECTION) {
308 variables.undeclareVariable(qName.toString());
309 } else {
310 if (index < 0) {
311 throw new JXPathInvalidAccessException("Index is less than 1: " + asPath());
312 }
313 Object collection = getBaseValue();
314 if (collection != null && index < getLength()) {
315 collection = ValueUtils.remove(collection, index);
316 variables.declareVariable(qName.toString(), collection);
317 }
318 }
319 }
320 }
321
322 @Override
323 public void setIndex(final int index) {
324 super.setIndex(index);
325 valuePointer = null;
326 }
327
328 @Override
329 public void setValue(final Object value) {
330 if (!actual) {
331 throw new JXPathException("Cannot set undefined variable: " + qName);
332 }
333 valuePointer = null;
334 if (index != WHOLE_COLLECTION) {
335 final Object collection = getBaseValue();
336 ValueUtils.setValue(collection, index, value);
337 } else {
338 variables.declareVariable(qName.toString(), value);
339 }
340 }
341
342 @Override
343 public boolean testNode(final NodeTest nodeTest) {
344 return getValuePointer().testNode(nodeTest);
345 }
346 }