View Javadoc

1   /*
2    * Copyright 1999-2004 The Apache Software Foundation
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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             // Assert: actual == true
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 }