1 package org.apache.commons.ognl;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.commons.ognl.enhance.ExpressionCompiler;
23 import org.apache.commons.ognl.enhance.UnsupportedCompilationException;
24
25 import java.beans.IndexedPropertyDescriptor;
26 import java.beans.PropertyDescriptor;
27 import java.lang.reflect.Method;
28 import java.util.Iterator;
29
30
31
32
33
34
35
36 public class ASTProperty
37 extends SimpleNode
38 implements NodeType
39 {
40 private boolean indexedAccess = false;
41
42 private Class getterClass;
43
44 private Class setterClass;
45
46 public ASTProperty( int id )
47 {
48 super( id );
49 }
50
51 public void setIndexedAccess( boolean value )
52 {
53 indexedAccess = value;
54 }
55
56
57
58
59
60
61 public boolean isIndexedAccess()
62 {
63 return indexedAccess;
64 }
65
66
67
68
69
70
71
72
73
74
75 public int getIndexedPropertyType( OgnlContext context, Object source )
76 throws OgnlException
77 {
78 Class type = context.getCurrentType();
79 Class prevType = context.getPreviousType();
80 try
81 {
82 if ( !isIndexedAccess() )
83 {
84 Object property = getProperty( context, source );
85
86 if ( property instanceof String )
87 {
88 return OgnlRuntime.getIndexedPropertyType( context, ( source == null )
89 ? null
90 : OgnlRuntime.getCompiler( context ).getInterfaceClass( source.getClass() ),
91 (String) property );
92 }
93 }
94
95 return OgnlRuntime.INDEXED_PROPERTY_NONE;
96 }
97 finally
98 {
99 context.setCurrentObject( source );
100 context.setCurrentType( type );
101 context.setPreviousType( prevType );
102 }
103 }
104
105 public Object getProperty( OgnlContext context, Object source )
106 throws OgnlException
107 {
108 return children[0].getValue( context, context.getRoot() );
109 }
110
111 protected Object getValueBody( OgnlContext context, Object source )
112 throws OgnlException
113 {
114 Object property = getProperty( context, source );
115
116 Object result = OgnlRuntime.getProperty( context, source, property );
117
118 if ( result == null )
119 {
120 result =
121 OgnlRuntime.getNullHandler( OgnlRuntime.getTargetClass( source ) ).nullPropertyValue( context, source,
122 property );
123 }
124
125 return result;
126 }
127
128 protected void setValueBody( OgnlContext context, Object target, Object value )
129 throws OgnlException
130 {
131 OgnlRuntime.setProperty( context, target, getProperty( context, target ), value );
132 }
133
134 public boolean isNodeSimpleProperty( OgnlContext context )
135 throws OgnlException
136 {
137 return ( children != null ) && ( children.length == 1 ) && ( (SimpleNode) children[0] ).isConstant( context );
138 }
139
140 public Class getGetterClass()
141 {
142 return getterClass;
143 }
144
145 public Class getSetterClass()
146 {
147 return setterClass;
148 }
149
150 public String toGetSourceString( OgnlContext context, Object target )
151 {
152 if ( context.getCurrentObject() == null )
153 {
154 throw new UnsupportedCompilationException( "Current target is null." );
155 }
156 String result = "";
157 Method m = null;
158
159 try
160 {
161
162
163
164
165
166
167 Node child = children[0];
168 if ( isIndexedAccess() )
169 {
170 Object value = child.getValue( context, context.getRoot() );
171
172 if ( value == null || DynamicSubscript.class.isAssignableFrom( value.getClass() ) )
173 {
174 throw new UnsupportedCompilationException(
175 "Value passed as indexed property was null or not supported." );
176 }
177
178
179 String srcString = getSourceString( context, child );
180
181 if ( context.get( "_indexedMethod" ) != null )
182 {
183 m = (Method) context.remove( "_indexedMethod" );
184 getterClass = m.getReturnType();
185
186 Object indexedValue = OgnlRuntime.callMethod( context, target, m.getName(), new Object[]{ value } );
187
188 context.setCurrentType( getterClass );
189 context.setCurrentObject( indexedValue );
190 context.setCurrentAccessor(
191 OgnlRuntime.getCompiler( context ).getSuperOrInterfaceClass( m, m.getDeclaringClass() ) );
192
193 return "." + m.getName() + "(" + srcString + ")";
194 }
195 else
196 {
197 PropertyAccessor propertyAccessor = OgnlRuntime.getPropertyAccessor( target.getClass() );
198
199
200
201
202
203 Object currentObject = context.getCurrentObject();
204 if ( ASTConst.class.isInstance( child ) && Number.class.isInstance( currentObject ) )
205 {
206 context.setCurrentType( OgnlRuntime.getPrimitiveWrapperClass( currentObject.getClass() ) );
207 }
208 Object indexValue = propertyAccessor.getProperty( context, target, value );
209 result = propertyAccessor.getSourceAccessor( context, target, srcString );
210 getterClass = context.getCurrentType();
211 context.setCurrentObject( indexValue );
212
213 return result;
214 }
215 }
216
217 String name = ( (ASTConst) child ).getValue().toString();
218
219 target = getTarget( context, target, name );
220
221 PropertyDescriptor pd = OgnlRuntime.getPropertyDescriptor( context.getCurrentObject().getClass(), name );
222
223 if ( pd != null && pd.getReadMethod() != null && !context.getMemberAccess().isAccessible( context,
224 context.getCurrentObject(),
225 pd.getReadMethod(),
226 name ) )
227 {
228 throw new UnsupportedCompilationException( "Member access forbidden for property " + name + " on class "
229 + context.getCurrentObject().getClass() );
230 }
231
232 if ( this.getIndexedPropertyType( context, context.getCurrentObject() ) > 0 && pd != null )
233 {
234
235
236 if ( pd instanceof IndexedPropertyDescriptor )
237 {
238 m = ( (IndexedPropertyDescriptor) pd ).getIndexedReadMethod();
239 }
240 else
241 {
242 if ( pd instanceof ObjectIndexedPropertyDescriptor )
243 {
244 m = ( (ObjectIndexedPropertyDescriptor) pd ).getIndexedReadMethod();
245 }
246 else
247 {
248 throw new OgnlException( "property '" + name + "' is not an indexed property" );
249 }
250 }
251
252 if ( parent == null )
253 {
254
255
256 m = OgnlRuntime.getReadMethod( context.getCurrentObject().getClass(), name );
257
258 result = m.getName() + "()";
259 getterClass = m.getReturnType();
260 }
261 else
262 {
263 context.put( "_indexedMethod", m );
264 }
265 }
266 else
267 {
268
269
270
271
272
273
274
275
276 PropertyAccessor pa = OgnlRuntime.getPropertyAccessor( context.getCurrentObject().getClass() );
277
278 if ( context.getCurrentObject().getClass().isArray() )
279 {
280 if ( pd == null )
281 {
282 pd = OgnlRuntime.getProperty( context.getCurrentObject().getClass(), name );
283
284 if ( pd != null && pd.getReadMethod() != null )
285 {
286 m = pd.getReadMethod();
287 result = pd.getName();
288 }
289 else
290 {
291 getterClass = int.class;
292 context.setCurrentAccessor( context.getCurrentObject().getClass() );
293 context.setCurrentType( int.class );
294 result = "." + name;
295 }
296 }
297 }
298 else
299 {
300 if ( pd != null && pd.getReadMethod() != null )
301 {
302 m = pd.getReadMethod();
303 result = "." + m.getName() + "()";
304 }
305 else if ( pa != null )
306 {
307 Object currObj = context.getCurrentObject();
308 Class currType = context.getCurrentType();
309 Class prevType = context.getPreviousType();
310
311 String srcString = child.toGetSourceString( context, context.getRoot() );
312
313 if ( ASTConst.class.isInstance( child ) && String.class.isInstance(
314 context.getCurrentObject() ) )
315 {
316 srcString = "\"" + srcString + "\"";
317 }
318 context.setCurrentObject( currObj );
319 context.setCurrentType( currType );
320 context.setPreviousType( prevType );
321
322 result = pa.getSourceAccessor( context, context.getCurrentObject(), srcString );
323
324 getterClass = context.getCurrentType();
325 }
326 }
327 }
328
329 }
330 catch ( Throwable t )
331 {
332 throw OgnlOps.castToRuntime( t );
333 }
334
335
336
337 if ( m != null )
338 {
339 getterClass = m.getReturnType();
340
341 context.setCurrentType( m.getReturnType() );
342 context.setCurrentAccessor(
343 OgnlRuntime.getCompiler( context ).getSuperOrInterfaceClass( m, m.getDeclaringClass() ) );
344 }
345
346 context.setCurrentObject( target );
347
348 return result;
349 }
350
351 Object getTarget( OgnlContext context, Object target, String name )
352 throws OgnlException
353 {
354 Class<?> clazz = context.getCurrentObject().getClass();
355 if ( !Iterator.class.isAssignableFrom( clazz ) || ( Iterator.class.isAssignableFrom( clazz ) && !name.contains(
356 "next" ) ) )
357 {
358 Object currObj = target;
359
360 try
361 {
362 target = getValue( context, context.getCurrentObject() );
363 }
364 catch ( NoSuchPropertyException e )
365 {
366 try
367 {
368 target = getValue( context, context.getRoot() );
369 }
370 catch ( NoSuchPropertyException ex )
371 {
372
373 }
374 }
375 finally
376 {
377 context.setCurrentObject( currObj );
378 }
379 }
380 return target;
381 }
382
383 Method getIndexedWriteMethod( PropertyDescriptor pd )
384 {
385 if ( IndexedPropertyDescriptor.class.isInstance( pd ) )
386 {
387 return ( (IndexedPropertyDescriptor) pd ).getIndexedWriteMethod();
388 }
389 else if ( ObjectIndexedPropertyDescriptor.class.isInstance( pd ) )
390 {
391 return ( (ObjectIndexedPropertyDescriptor) pd ).getIndexedWriteMethod();
392 }
393
394 return null;
395 }
396
397 public String toSetSourceString( OgnlContext context, Object target )
398 {
399 String result = "";
400 Method m = null;
401
402 if ( context.getCurrentObject() == null )
403 {
404 throw new UnsupportedCompilationException( "Current target is null." );
405 }
406
407
408
409
410
411
412 try
413 {
414
415 Node child = children[0];
416 if ( isIndexedAccess() )
417 {
418 Object value = child.getValue( context, context.getRoot() );
419
420 if ( value == null )
421 {
422 throw new UnsupportedCompilationException(
423 "Value passed as indexed property is null, can't enhance statement to bytecode." );
424 }
425
426 String srcString = getSourceString( context, child );
427
428
429
430
431 if ( context.get( "_indexedMethod" ) != null )
432 {
433 m = (Method) context.remove( "_indexedMethod" );
434 PropertyDescriptor pd = (PropertyDescriptor) context.remove( "_indexedDescriptor" );
435
436 boolean lastChild = lastChild( context );
437 if ( lastChild )
438 {
439 m = getIndexedWriteMethod( pd );
440
441 if ( m == null )
442 {
443 throw new UnsupportedCompilationException(
444 "Indexed property has no corresponding write method." );
445 }
446 }
447
448 setterClass = m.getParameterTypes()[0];
449
450 Object indexedValue = null;
451 if ( !lastChild )
452 {
453 indexedValue = OgnlRuntime.callMethod( context, target, m.getName(), new Object[]{ value } );
454 }
455 context.setCurrentType( setterClass );
456 context.setCurrentAccessor(
457 OgnlRuntime.getCompiler( context ).getSuperOrInterfaceClass( m, m.getDeclaringClass() ) );
458
459 if ( !lastChild )
460 {
461 context.setCurrentObject( indexedValue );
462 return "." + m.getName() + "(" + srcString + ")";
463 }
464 else
465 {
466 return "." + m.getName() + "(" + srcString + ", $3)";
467 }
468 }
469 else
470 {
471 PropertyAccessor propertyAccessor = OgnlRuntime.getPropertyAccessor( target.getClass() );
472
473 Object currentObject = context.getCurrentObject();
474 if ( ASTConst.class.isInstance( child ) && Number.class.isInstance( currentObject ) )
475 {
476 context.setCurrentType( OgnlRuntime.getPrimitiveWrapperClass( currentObject.getClass() ) );
477 }
478 Object indexValue = propertyAccessor.getProperty( context, target, value );
479 result = lastChild( context )
480 ? propertyAccessor.getSourceSetter( context, target, srcString )
481 : propertyAccessor.getSourceAccessor( context, target, srcString );
482
483
484
485
486
487
488
489 getterClass = context.getCurrentType();
490 context.setCurrentObject( indexValue );
491
492
493
494
495
496
497
498
499
500 return result;
501 }
502 }
503
504 String name = ( (ASTConst) child ).getValue().toString();
505
506
507
508
509 target = getTarget( context, target, name );
510
511 PropertyDescriptor pd = OgnlRuntime.getPropertyDescriptor(
512 OgnlRuntime.getCompiler( context ).getInterfaceClass( context.getCurrentObject().getClass() ), name );
513
514 if ( pd != null )
515 {
516 Method pdMethod = lastChild( context ) ? pd.getWriteMethod() : pd.getReadMethod();
517
518 if ( pdMethod != null && !context.getMemberAccess().isAccessible( context, context.getCurrentObject(),
519 pdMethod, name ) )
520 {
521 throw new UnsupportedCompilationException(
522 "Member access forbidden for property " + name + " on class "
523 + context.getCurrentObject().getClass() );
524 }
525 }
526
527 if ( pd != null && this.getIndexedPropertyType( context, context.getCurrentObject() ) > 0 )
528 {
529
530
531 if ( pd instanceof IndexedPropertyDescriptor )
532 {
533 IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
534 m = lastChild( context ) ? ipd.getIndexedWriteMethod() : ipd.getIndexedReadMethod();
535 }
536 else
537 {
538 if ( pd instanceof ObjectIndexedPropertyDescriptor )
539 {
540 ObjectIndexedPropertyDescriptor opd = (ObjectIndexedPropertyDescriptor) pd;
541
542 m = lastChild( context ) ? opd.getIndexedWriteMethod() : opd.getIndexedReadMethod();
543 }
544 else
545 {
546 throw new OgnlException( "property '" + name + "' is not an indexed property" );
547 }
548 }
549
550 if ( parent == null )
551 {
552
553
554
555 m = OgnlRuntime.getWriteMethod( context.getCurrentObject().getClass(), name );
556 Class parm = m.getParameterTypes()[0];
557 String cast = parm.isArray() ? ExpressionCompiler.getCastString( parm ) : parm.getName();
558
559 result = m.getName() + "((" + cast + ")$3)";
560 setterClass = parm;
561 }
562 else
563 {
564 context.put( "_indexedMethod", m );
565 context.put( "_indexedDescriptor", pd );
566 }
567
568 }
569 else
570 {
571 PropertyAccessor pa = OgnlRuntime.getPropertyAccessor( context.getCurrentObject().getClass() );
572
573
574
575
576
577
578 if ( target != null )
579 {
580 setterClass = target.getClass();
581 }
582 if ( parent != null && pd != null && pa == null )
583 {
584 m = pd.getReadMethod();
585 result = m.getName() + "()";
586 }
587 else
588 {
589 if ( context.getCurrentObject().getClass().isArray() )
590 {
591 result = "";
592 }
593 else if ( pa != null )
594 {
595 Object currObj = context.getCurrentObject();
596
597
598
599 String srcString = child.toGetSourceString( context, context.getRoot() );
600
601 if ( ASTConst.class.isInstance( child ) && String.class.isInstance(
602 context.getCurrentObject() ) )
603 {
604 srcString = "\"" + srcString + "\"";
605 }
606
607 context.setCurrentObject( currObj );
608
609
610
611 if ( !lastChild( context ) )
612 {
613 result = pa.getSourceAccessor( context, context.getCurrentObject(), srcString );
614 }
615 else
616 {
617 result = pa.getSourceSetter( context, context.getCurrentObject(), srcString );
618 }
619
620 getterClass = context.getCurrentType();
621 }
622 }
623 }
624
625 }
626 catch ( Throwable t )
627 {
628 throw OgnlOps.castToRuntime( t );
629 }
630
631 context.setCurrentObject( target );
632
633 if ( m != null )
634 {
635 context.setCurrentType( m.getReturnType() );
636 context.setCurrentAccessor(
637 OgnlRuntime.getCompiler( context ).getSuperOrInterfaceClass( m, m.getDeclaringClass() ) );
638 }
639
640 return result;
641 }
642
643 public <R, P> R accept( NodeVisitor<? extends R, ? super P> visitor, P data )
644 throws OgnlException
645 {
646 return visitor.visit( this, data );
647 }
648
649 private static String getSourceString( OgnlContext context, Node child )
650 {
651 String srcString = child.toGetSourceString( context, context.getRoot() );
652 srcString = ExpressionCompiler.getRootExpression( child, context.getRoot(), context ) + srcString;
653
654 if ( ASTChain.class.isInstance( child ) )
655 {
656 String cast = (String) context.remove( ExpressionCompiler.PRE_CAST );
657 if ( cast != null )
658 {
659 srcString = cast + srcString;
660 }
661 }
662
663 if ( ASTConst.class.isInstance( child ) && String.class.isInstance( context.getCurrentObject() ) )
664 {
665 srcString = "\"" + srcString + "\"";
666 }
667
668
669 return srcString;
670 }
671
672 }