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