001 package org.apache.commons.ognl; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022 import org.apache.commons.ognl.enhance.ExpressionAccessor; 023 024 import java.io.PrintWriter; 025 import java.io.Serializable; 026 027 /** 028 * @author Luke Blanshard (blanshlu@netscape.net) 029 * @author Drew Davidson (drew@ognl.org) 030 */ 031 public abstract class SimpleNode 032 implements Node, Serializable 033 { 034 035 private static final long serialVersionUID = 8305393337889433901L; 036 037 protected Node parent; 038 039 protected Node[] children; 040 041 protected int id; 042 043 protected OgnlParser parser; 044 045 private boolean constantValueCalculated; 046 047 private volatile boolean hasConstantValue; 048 049 private Object constantValue; 050 051 private ExpressionAccessor accessor; 052 053 public SimpleNode( int i ) 054 { 055 id = i; 056 } 057 058 public SimpleNode( OgnlParser p, int i ) 059 { 060 this( i ); 061 parser = p; 062 } 063 064 public void jjtOpen() 065 { 066 } 067 068 public void jjtClose() 069 { 070 } 071 072 public void jjtSetParent( Node n ) 073 { 074 parent = n; 075 } 076 077 public Node jjtGetParent() 078 { 079 return parent; 080 } 081 082 public void jjtAddChild( Node n, int i ) 083 { 084 if ( children == null ) 085 { 086 children = new Node[i + 1]; 087 } 088 else if ( i >= children.length ) 089 { 090 Node c[] = new Node[i + 1]; 091 System.arraycopy( children, 0, c, 0, children.length ); 092 children = c; 093 } 094 children[i] = n; 095 } 096 097 public Node jjtGetChild( int i ) 098 { 099 return children[i]; 100 } 101 102 public int jjtGetNumChildren() 103 { 104 return ( children == null ) ? 0 : children.length; 105 } 106 107 /* 108 * You can override these two methods in subclasses of SimpleNode to customize the way the node appears when the 109 * tree is dumped. If your output uses more than one line you should override toString(String), otherwise overriding 110 * toString() is probably all you need to do. 111 */ 112 113 @Override 114 public String toString() 115 { 116 final StringBuilder data = new StringBuilder(); 117 try 118 { 119 accept( ToStringVisitor.INSTANCE, data ); 120 } 121 catch ( OgnlException e ) 122 { 123 // ignored. 124 } 125 return data.toString(); 126 } 127 128 // OGNL additions 129 130 public String toString( String prefix ) 131 { 132 return prefix + OgnlParserTreeConstants.jjtNodeName[id] + " " + toString(); 133 } 134 135 public String toGetSourceString( OgnlContext context, Object target ) 136 { 137 return toString(); 138 } 139 140 public String toSetSourceString( OgnlContext context, Object target ) 141 { 142 return toString(); 143 } 144 145 /* 146 * Override this method if you want to customize how the node dumps out its children. 147 */ 148 149 public void dump( PrintWriter writer, String prefix ) 150 { 151 writer.println( toString( prefix ) ); 152 153 if ( children != null ) 154 { 155 for ( int i = 0; i < children.length; ++i ) 156 { 157 SimpleNode n = (SimpleNode) children[i]; 158 if ( n != null ) 159 { 160 n.dump( writer, prefix + " " ); 161 } 162 } 163 } 164 } 165 166 public int getIndexInParent() 167 { 168 int result = -1; 169 170 if ( parent != null ) 171 { 172 int icount = parent.jjtGetNumChildren(); 173 174 for ( int i = 0; i < icount; i++ ) 175 { 176 if ( parent.jjtGetChild( i ) == this ) 177 { 178 result = i; 179 break; 180 } 181 } 182 } 183 184 return result; 185 } 186 187 public Node getNextSibling() 188 { 189 Node result = null; 190 int i = getIndexInParent(); 191 192 if ( i >= 0 ) 193 { 194 int icount = parent.jjtGetNumChildren(); 195 196 if ( i < icount ) 197 { 198 result = parent.jjtGetChild( i + 1 ); 199 } 200 } 201 return result; 202 } 203 204 protected Object evaluateGetValueBody( OgnlContext context, Object source ) 205 throws OgnlException 206 { 207 context.setCurrentObject( source ); 208 context.setCurrentNode( this ); 209 210 if ( !constantValueCalculated ) 211 { 212 constantValueCalculated = true; 213 boolean constant = isConstant( context ); 214 215 if ( constant ) 216 { 217 constantValue = getValueBody( context, source ); 218 } 219 220 hasConstantValue = constant; 221 } 222 223 return hasConstantValue ? constantValue : getValueBody( context, source ); 224 } 225 226 protected void evaluateSetValueBody( OgnlContext context, Object target, Object value ) 227 throws OgnlException 228 { 229 context.setCurrentObject( target ); 230 context.setCurrentNode( this ); 231 setValueBody( context, target, value ); 232 } 233 234 public final Object getValue( OgnlContext context, Object source ) 235 throws OgnlException 236 { 237 Object result = null; 238 239 if ( context.getTraceEvaluations() ) 240 { 241 242 EvaluationPool pool = OgnlRuntime.getEvaluationPool(); 243 Throwable evalException = null; 244 Evaluation evaluation = pool.create( this, source ); 245 246 context.pushEvaluation( evaluation ); 247 try 248 { 249 result = evaluateGetValueBody( context, source ); 250 } 251 catch ( OgnlException ex ) 252 { 253 evalException = ex; 254 throw ex; 255 } 256 catch ( RuntimeException ex ) 257 { 258 evalException = ex; 259 throw ex; 260 } 261 finally 262 { 263 Evaluation eval = context.popEvaluation(); 264 265 eval.setResult( result ); 266 if ( evalException != null ) 267 { 268 eval.setException( evalException ); 269 } 270 if ( ( evalException == null ) && ( context.getRootEvaluation() == null ) 271 && !context.getKeepLastEvaluation() ) 272 { 273 pool.recycleAll( eval ); 274 } 275 } 276 } 277 else 278 { 279 result = evaluateGetValueBody( context, source ); 280 } 281 282 return result; 283 } 284 285 /** 286 * Subclasses implement this method to do the actual work of extracting the appropriate value from the source 287 * object. 288 */ 289 protected abstract Object getValueBody( OgnlContext context, Object source ) 290 throws OgnlException; 291 292 public final void setValue( OgnlContext context, Object target, Object value ) 293 throws OgnlException 294 { 295 if ( context.getTraceEvaluations() ) 296 { 297 EvaluationPool pool = OgnlRuntime.getEvaluationPool(); 298 Throwable evalException = null; 299 Evaluation evaluation = pool.create( this, target, true ); 300 301 context.pushEvaluation( evaluation ); 302 try 303 { 304 evaluateSetValueBody( context, target, value ); 305 } 306 catch ( OgnlException ex ) 307 { 308 evalException = ex; 309 ex.setEvaluation( evaluation ); 310 throw ex; 311 } 312 catch ( RuntimeException ex ) 313 { 314 evalException = ex; 315 throw ex; 316 } 317 finally 318 { 319 Evaluation eval = context.popEvaluation(); 320 321 if ( evalException != null ) 322 { 323 eval.setException( evalException ); 324 } 325 if ( ( evalException == null ) && ( context.getRootEvaluation() == null ) 326 && !context.getKeepLastEvaluation() ) 327 { 328 pool.recycleAll( eval ); 329 } 330 } 331 } 332 else 333 { 334 evaluateSetValueBody( context, target, value ); 335 } 336 } 337 338 /** 339 * Subclasses implement this method to do the actual work of setting the appropriate value in the target object. The 340 * default implementation throws an <code>InappropriateExpressionException</code>, meaning that it cannot be a set 341 * expression. 342 */ 343 protected void setValueBody( OgnlContext context, Object target, Object value ) 344 throws OgnlException 345 { 346 throw new InappropriateExpressionException( this ); 347 } 348 349 /** Returns true iff this node is constant without respect to the children. */ 350 public boolean isNodeConstant( OgnlContext context ) 351 throws OgnlException 352 { 353 return false; 354 } 355 356 public boolean isConstant( OgnlContext context ) 357 throws OgnlException 358 { 359 return isNodeConstant( context ); 360 } 361 362 public boolean isNodeSimpleProperty( OgnlContext context ) 363 throws OgnlException 364 { 365 return false; 366 } 367 368 public boolean isSimpleProperty( OgnlContext context ) 369 throws OgnlException 370 { 371 return isNodeSimpleProperty( context ); 372 } 373 374 public boolean isSimpleNavigationChain( OgnlContext context ) 375 throws OgnlException 376 { 377 return isSimpleProperty( context ); 378 } 379 380 public boolean isEvalChain( OgnlContext context ) 381 throws OgnlException 382 { 383 if ( children == null ) 384 { 385 return false; 386 } 387 for ( Node child : children ) 388 { 389 if ( child instanceof SimpleNode ) 390 { 391 if ( ( (SimpleNode) child ).isEvalChain( context ) ) 392 { 393 return true; 394 } 395 } 396 } 397 return false; 398 } 399 400 protected boolean lastChild( OgnlContext context ) 401 { 402 return parent == null || context.get( "_lastChild" ) != null; 403 } 404 405 /** 406 * This method may be called from subclasses' jjtClose methods. It flattens the tree under this node by eliminating 407 * any children that are of the same class as this node and copying their children to this node. 408 */ 409 protected void flattenTree() 410 { 411 boolean shouldFlatten = false; 412 int newSize = 0; 413 414 for ( Node aChildren : children ) 415 { 416 if ( aChildren.getClass() == getClass() ) 417 { 418 shouldFlatten = true; 419 newSize += aChildren.jjtGetNumChildren(); 420 } 421 else 422 { 423 ++newSize; 424 } 425 } 426 427 if ( shouldFlatten ) 428 { 429 Node[] newChildren = new Node[newSize]; 430 int j = 0; 431 432 for ( Node c : children ) 433 { 434 if ( c.getClass() == getClass() ) 435 { 436 for ( int k = 0; k < c.jjtGetNumChildren(); ++k ) 437 { 438 newChildren[j++] = c.jjtGetChild( k ); 439 } 440 441 } 442 else 443 { 444 newChildren[j++] = c; 445 } 446 } 447 448 if ( j != newSize ) 449 { 450 throw new Error( "Assertion error: " + j + " != " + newSize ); 451 } 452 453 children = newChildren; 454 } 455 } 456 457 public ExpressionAccessor getAccessor() 458 { 459 return accessor; 460 } 461 462 public void setAccessor( ExpressionAccessor accessor ) 463 { 464 this.accessor = accessor; 465 } 466 }