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 */ 017package org.apache.commons.jxpath.ri.model; 018 019import org.apache.commons.jxpath.AbstractFactory; 020import org.apache.commons.jxpath.JXPathAbstractFactoryException; 021import org.apache.commons.jxpath.JXPathContext; 022import org.apache.commons.jxpath.JXPathException; 023import org.apache.commons.jxpath.JXPathIntrospector; 024import org.apache.commons.jxpath.JXPathInvalidAccessException; 025import org.apache.commons.jxpath.Variables; 026import org.apache.commons.jxpath.ri.QName; 027import org.apache.commons.jxpath.ri.compiler.NodeTest; 028import org.apache.commons.jxpath.ri.model.beans.NullPointer; 029import 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 22:02:00 +0200 (Fr, 02 Mai 2008) $ 036 */ 037public 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}