001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.jxpath.ri.model;
017:
018: import org.apache.commons.jxpath.AbstractFactory;
019: import org.apache.commons.jxpath.JXPathContext;
020: import org.apache.commons.jxpath.JXPathException;
021: import org.apache.commons.jxpath.JXPathIntrospector;
022: import org.apache.commons.jxpath.Variables;
023: import org.apache.commons.jxpath.ri.QName;
024: import org.apache.commons.jxpath.ri.compiler.NodeTest;
025: import org.apache.commons.jxpath.ri.model.beans.NullPointer;
026: import org.apache.commons.jxpath.util.ValueUtils;
027:
028: /**
029: * Pointer to a context variable.
030: *
031: * @author Dmitri Plotnikov
032: * @version $Revision: 1.18 $ $Date: 2004/04/04 22:06:36 $
033: */
034: public class VariablePointer extends NodePointer {
035: private Variables variables;
036: private QName name;
037: private NodePointer valuePointer;
038: private boolean actual;
039:
040: public VariablePointer(Variables variables, QName name) {
041: super (null);
042: this .variables = variables;
043: this .name = name;
044: actual = true;
045: }
046:
047: public VariablePointer(QName name) {
048: super (null);
049: this .name = name;
050: actual = false;
051: }
052:
053: public boolean isContainer() {
054: return true;
055: }
056:
057: public QName getName() {
058: return name;
059: }
060:
061: public Object getBaseValue() {
062: if (!actual) {
063: throw new JXPathException("Undefined variable: " + name);
064: }
065: return variables.getVariable(name.toString());
066: }
067:
068: public boolean isLeaf() {
069: Object value = getNode();
070: return value == null
071: || JXPathIntrospector.getBeanInfo(value.getClass())
072: .isAtomic();
073: }
074:
075: public boolean isCollection() {
076: Object value = getBaseValue();
077: return value != null && ValueUtils.isCollection(value);
078: }
079:
080: public Object getImmediateNode() {
081: Object value = getBaseValue();
082: if (index != WHOLE_COLLECTION) {
083: return ValueUtils.getValue(value, index);
084: } else {
085: return ValueUtils.getValue(value);
086: }
087: }
088:
089: public void setValue(Object value) {
090: if (!actual) {
091: throw new JXPathException("Cannot set undefined variable: "
092: + name);
093: }
094: valuePointer = null;
095: if (index != WHOLE_COLLECTION) {
096: Object collection = getBaseValue();
097: ValueUtils.setValue(collection, index, value);
098: } else {
099: 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 = NodePointer.newChildNodePointer(this ,
118: null, value);
119: } else {
120: return new NullPointer(this , getName()) {
121: public Object getImmediateNode() {
122: throw new JXPathException(
123: "Undefined variable: " + name);
124: }
125: };
126: }
127: }
128: return valuePointer;
129: }
130:
131: public int getLength() {
132: if (actual) {
133: Object value = getBaseValue();
134: if (value == null) {
135: return 1;
136: }
137: return ValueUtils.getLength(value);
138: }
139: return 0;
140: }
141:
142: public NodePointer createPath(JXPathContext context, Object value) {
143: if (actual) {
144: setValue(value);
145: return this ;
146: }
147: NodePointer ptr = createPath(context);
148: ptr.setValue(value);
149: return ptr;
150: }
151:
152: public NodePointer createPath(JXPathContext context) {
153: if (!actual) {
154: AbstractFactory factory = getAbstractFactory(context);
155: if (!factory.declareVariable(context, name.toString())) {
156: throw new JXPathException(
157: "Factory cannot define variable '" + name
158: + "' for path: " + asPath());
159: }
160: findVariables(context);
161: // Assert: actual == true
162: }
163: return this ;
164: }
165:
166: public NodePointer createChild(JXPathContext context, QName name,
167: int index) {
168: Object collection = createCollection(context, index);
169: if (!isActual() || (index != 0 && index != WHOLE_COLLECTION)) {
170: AbstractFactory factory = getAbstractFactory(context);
171: boolean success = factory.createObject(context, this ,
172: collection, getName().toString(), index);
173: if (!success) {
174: throw new JXPathException(
175: "Factory could not create object path: "
176: + asPath());
177: }
178: NodePointer cln = (NodePointer) clone();
179: cln.setIndex(index);
180: return cln;
181: }
182: return this ;
183: }
184:
185: public NodePointer createChild(JXPathContext context, QName name,
186: int index, Object value) {
187: Object collection = createCollection(context, index);
188: ValueUtils.setValue(collection, index, value);
189: NodePointer cl = (NodePointer) clone();
190: cl.setIndex(index);
191: return cl;
192: }
193:
194: private Object createCollection(JXPathContext context, int index) {
195: createPath(context);
196:
197: Object collection = getBaseValue();
198: if (collection == null) {
199: throw new JXPathException(
200: "Factory did not assign a collection to variable '"
201: + name + "' for path: " + asPath());
202: }
203:
204: if (index == WHOLE_COLLECTION) {
205: index = 0;
206: } else if (index < 0) {
207: throw new JXPathException("Index is less than 1: "
208: + asPath());
209: }
210:
211: if (index >= getLength()) {
212: collection = ValueUtils.expandCollection(collection,
213: index + 1);
214: variables.declareVariable(name.toString(), collection);
215: }
216:
217: return collection;
218: }
219:
220: public void remove() {
221: if (actual) {
222: if (index == WHOLE_COLLECTION) {
223: variables.undeclareVariable(name.toString());
224: } else {
225: if (index < 0) {
226: throw new JXPathException("Index is less than 1: "
227: + asPath());
228: }
229:
230: Object collection = getBaseValue();
231: if (collection != null && index < getLength()) {
232: collection = ValueUtils.remove(collection, index);
233: variables.declareVariable(name.toString(),
234: collection);
235: }
236: }
237: }
238: }
239:
240: protected void findVariables(JXPathContext context) {
241: valuePointer = null;
242: JXPathContext varCtx = context;
243: while (varCtx != null) {
244: variables = varCtx.getVariables();
245: if (variables.isDeclaredVariable(name.toString())) {
246: actual = true;
247: break;
248: }
249: varCtx = varCtx.getParentContext();
250: variables = null;
251: }
252: }
253:
254: public int hashCode() {
255: return (actual ? System.identityHashCode(variables) : 0)
256: + name.hashCode() + index;
257: }
258:
259: public boolean equals(Object object) {
260: if (object == this ) {
261: return true;
262: }
263:
264: if (!(object instanceof VariablePointer)) {
265: return false;
266: }
267:
268: VariablePointer other = (VariablePointer) object;
269: return variables == other.variables && name.equals(other.name)
270: && index == other.index;
271: }
272:
273: public String asPath() {
274: StringBuffer buffer = new StringBuffer();
275: buffer.append('$');
276: buffer.append(name);
277: if (!actual) {
278: if (index != WHOLE_COLLECTION) {
279: buffer.append('[').append(index + 1).append(']');
280: }
281: } else if (index != WHOLE_COLLECTION
282: && (getNode() == null || isCollection())) {
283: buffer.append('[').append(index + 1).append(']');
284: }
285: return buffer.toString();
286: }
287:
288: public NodeIterator childIterator(NodeTest test, boolean reverse,
289: NodePointer startWith) {
290: return getValuePointer()
291: .childIterator(test, reverse, startWith);
292: }
293:
294: public NodeIterator attributeIterator(QName name) {
295: return getValuePointer().attributeIterator(name);
296: }
297:
298: public NodeIterator namespaceIterator() {
299: return getValuePointer().namespaceIterator();
300: }
301:
302: public NodePointer namespacePointer(String name) {
303: return getValuePointer().namespacePointer(name);
304: }
305:
306: public boolean testNode(NodeTest nodeTest) {
307: return getValuePointer().testNode(nodeTest);
308: }
309:
310: private AbstractFactory getAbstractFactory(JXPathContext context) {
311: AbstractFactory factory = context.getFactory();
312: if (factory == null) {
313: throw new JXPathException(
314: "Factory is not set on the JXPathContext - cannot create path: "
315: + asPath());
316: }
317: return factory;
318: }
319:
320: public int compareChildNodePointers(NodePointer pointer1,
321: NodePointer pointer2) {
322: return pointer1.getIndex() - pointer2.getIndex();
323: }
324: }
|