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