1 package org.apache.commons.ognl.enhance;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import javassist.CannotCompileException;
23 import javassist.ClassPool;
24 import javassist.CtClass;
25 import javassist.CtField;
26 import javassist.CtMethod;
27 import javassist.CtNewConstructor;
28 import javassist.CtNewMethod;
29 import javassist.LoaderClassPath;
30 import javassist.NotFoundException;
31 import org.apache.commons.ognl.ASTAnd;
32 import org.apache.commons.ognl.ASTChain;
33 import org.apache.commons.ognl.ASTConst;
34 import org.apache.commons.ognl.ASTCtor;
35 import org.apache.commons.ognl.ASTList;
36 import org.apache.commons.ognl.ASTMethod;
37 import org.apache.commons.ognl.ASTOr;
38 import org.apache.commons.ognl.ASTProperty;
39 import org.apache.commons.ognl.ASTRootVarRef;
40 import org.apache.commons.ognl.ASTStaticField;
41 import org.apache.commons.ognl.ASTStaticMethod;
42 import org.apache.commons.ognl.ASTThisVarRef;
43 import org.apache.commons.ognl.ASTVarRef;
44 import org.apache.commons.ognl.ClassResolver;
45 import org.apache.commons.ognl.ExpressionNode;
46 import org.apache.commons.ognl.Node;
47 import org.apache.commons.ognl.OgnlContext;
48 import org.apache.commons.ognl.OgnlRuntime;
49
50 import java.lang.reflect.Method;
51 import java.lang.reflect.Modifier;
52 import java.util.Collection;
53 import java.util.HashMap;
54 import java.util.Iterator;
55 import java.util.List;
56 import java.util.Map;
57 import java.util.Set;
58
59 import static java.lang.String.format;
60
61
62
63
64
65 public class ExpressionCompiler
66 implements OgnlExpressionCompiler
67 {
68
69
70
71
72 public static final String PRE_CAST = "_preCast";
73
74
75
76
77 protected Map<ClassResolver, EnhancedClassLoader> loaders = new HashMap<ClassResolver, EnhancedClassLoader>();
78
79
80
81
82 protected ClassPool pool;
83
84 protected int classCounter = 0;
85
86
87
88
89
90
91
92
93
94
95 public static void addCastString( OgnlContext context, String cast )
96 {
97 String value = (String) context.get( PRE_CAST );
98
99 if ( value != null )
100 {
101 value = cast + value;
102 }
103 else
104 {
105 value = cast;
106 }
107
108 context.put( PRE_CAST, value );
109 }
110
111
112
113
114
115
116
117
118
119
120
121 public static String getCastString( Class<?> type )
122 {
123 if ( type == null )
124 {
125 return null;
126 }
127
128 return type.isArray() ? type.getComponentType().getName() + "[]" : type.getName();
129 }
130
131
132
133
134
135
136
137
138
139
140
141
142 public static String getRootExpression( Node expression, Object root, OgnlContext context )
143 {
144 String rootExpr = "";
145
146 if ( !shouldCast( expression ) )
147 {
148 return rootExpr;
149 }
150
151 if ( ( !ASTList.class.isInstance( expression ) && !ASTVarRef.class.isInstance( expression )
152 && !ASTStaticMethod.class.isInstance( expression ) && !ASTStaticField.class.isInstance( expression )
153 && !ASTConst.class.isInstance( expression ) && !ExpressionNode.class.isInstance( expression )
154 && !ASTCtor.class.isInstance( expression ) && !ASTStaticMethod.class.isInstance( expression )
155 && root != null ) || ( root != null && ASTRootVarRef.class.isInstance( expression ) ) )
156 {
157
158 Class<?> castClass = OgnlRuntime.getCompiler( context ).getRootExpressionClass( expression, context );
159
160 if ( castClass.isArray() || ASTRootVarRef.class.isInstance( expression ) || ASTThisVarRef.class.isInstance(
161 expression ) )
162 {
163 rootExpr = "((" + getCastString( castClass ) + ")$2)";
164
165 if ( ASTProperty.class.isInstance( expression ) && !( (ASTProperty) expression ).isIndexedAccess() )
166 {
167 rootExpr += ".";
168 }
169 }
170 else if ( ( ASTProperty.class.isInstance( expression ) && ( (ASTProperty) expression ).isIndexedAccess() )
171 || ASTChain.class.isInstance( expression ) )
172 {
173 rootExpr = "((" + getCastString( castClass ) + ")$2)";
174 }
175 else
176 {
177 rootExpr = "((" + getCastString( castClass ) + ")$2).";
178 }
179 }
180
181 return rootExpr;
182 }
183
184
185
186
187
188
189
190
191 public static boolean shouldCast( Node expression )
192 {
193 if ( ASTChain.class.isInstance( expression ) )
194 {
195 Node child = expression.jjtGetChild( 0 );
196 if ( ASTConst.class.isInstance( child ) || ASTStaticMethod.class.isInstance( child )
197 || ASTStaticField.class.isInstance( child ) || ( ASTVarRef.class.isInstance( child )
198 && !ASTRootVarRef.class.isInstance( child ) ) )
199 {
200 return false;
201 }
202 }
203
204 return !ASTConst.class.isInstance( expression );
205 }
206
207
208
209
210 public String castExpression( OgnlContext context, Node expression, String body )
211 {
212
213
214 if ( context.getCurrentAccessor() == null || context.getPreviousType() == null
215 || context.getCurrentAccessor().isAssignableFrom( context.getPreviousType() ) || (
216 context.getCurrentType() != null && context.getCurrentObject() != null
217 && context.getCurrentType().isAssignableFrom( context.getCurrentObject().getClass() )
218 && context.getCurrentAccessor().isAssignableFrom( context.getPreviousType() ) ) || body == null
219 || body.trim().length() < 1 || ( context.getCurrentType() != null && context.getCurrentType().isArray() && (
220 context.getPreviousType() == null || context.getPreviousType() != Object.class ) )
221 || ASTOr.class.isInstance( expression ) || ASTAnd.class.isInstance( expression )
222 || ASTRootVarRef.class.isInstance( expression ) || context.getCurrentAccessor() == Class.class || (
223 context.get( ExpressionCompiler.PRE_CAST ) != null && ( (String) context.get(
224 ExpressionCompiler.PRE_CAST ) ).startsWith( "new" ) ) || ASTStaticField.class.isInstance( expression )
225 || ASTStaticMethod.class.isInstance( expression ) || ( OrderedReturn.class.isInstance( expression )
226 && ( (OrderedReturn) expression ).getLastExpression() != null ) )
227 {
228 return body;
229 }
230
231
232
233
234
235
236
237
238 ExpressionCompiler.addCastString( context,
239 "((" + ExpressionCompiler.getCastString( context.getCurrentAccessor() )
240 + ")" );
241
242 return ")" + body;
243 }
244
245
246
247
248 public String getClassName( Class<?> clazz )
249 {
250 if ( "java.util.AbstractList$Itr".equals( clazz.getName() ) )
251 {
252 return Iterator.class.getName();
253 }
254
255 if ( Modifier.isPublic( clazz.getModifiers() ) && clazz.isInterface() )
256 {
257 return clazz.getName();
258 }
259
260 Class<?>[] interfaces = clazz.getInterfaces();
261
262 for ( Class<?> intface : interfaces )
263 {
264 if ( intface.getName().indexOf( "util.List" ) > 0 )
265 {
266 return intface.getName();
267 }
268 else if ( intface.getName().indexOf( "Iterator" ) > 0 )
269 {
270 return intface.getName();
271 }
272 }
273
274 if ( clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0 )
275 {
276 return getClassName( clazz.getSuperclass() );
277 }
278
279 return clazz.getName();
280 }
281
282
283
284
285 public Class<?> getSuperOrInterfaceClass( Method m, Class<?> clazz )
286 {
287 if ( clazz.getInterfaces() != null && clazz.getInterfaces().length > 0 )
288 {
289 Class<?>[] intfs = clazz.getInterfaces();
290 Class<?> intClass;
291
292 for ( Class<?> intf : intfs )
293 {
294 intClass = getSuperOrInterfaceClass( m, intf );
295
296 if ( intClass != null )
297 {
298 return intClass;
299 }
300
301 if ( Modifier.isPublic( intf.getModifiers() ) && containsMethod( m, intf ) )
302 {
303 return intf;
304 }
305 }
306 }
307
308 if ( clazz.getSuperclass() != null )
309 {
310 Class<?> superClass = getSuperOrInterfaceClass( m, clazz.getSuperclass() );
311
312 if ( superClass != null )
313 {
314 return superClass;
315 }
316 }
317
318 if ( Modifier.isPublic( clazz.getModifiers() ) && containsMethod( m, clazz ) )
319 {
320 return clazz;
321 }
322
323 return null;
324 }
325
326
327
328
329
330
331
332
333
334 public boolean containsMethod( Method m, Class<?> clazz )
335 {
336 Method[] methods = clazz.getMethods();
337
338 if ( methods == null )
339 {
340 return false;
341 }
342
343 for ( Method method : methods )
344 {
345 if ( method.getName().equals( m.getName() ) && method.getReturnType() == m.getReturnType() )
346 {
347 Class<?>[] parms = m.getParameterTypes();
348 if ( parms == null )
349 {
350 continue;
351 }
352
353 Class<?>[] mparms = method.getParameterTypes();
354 if ( mparms == null || mparms.length != parms.length )
355 {
356 continue;
357 }
358
359 boolean parmsMatch = true;
360 for ( int p = 0; p < parms.length; p++ )
361 {
362 if ( parms[p] != mparms[p] )
363 {
364 parmsMatch = false;
365 break;
366 }
367 }
368
369 if ( !parmsMatch )
370 {
371 continue;
372 }
373
374 Class<?>[] exceptions = m.getExceptionTypes();
375 if ( exceptions == null )
376 {
377 continue;
378 }
379
380 Class<?>[] mexceptions = method.getExceptionTypes();
381 if ( mexceptions == null || mexceptions.length != exceptions.length )
382 {
383 continue;
384 }
385
386 boolean exceptionsMatch = true;
387 for ( int e = 0; e < exceptions.length; e++ )
388 {
389 if ( exceptions[e] != mexceptions[e] )
390 {
391 exceptionsMatch = false;
392 break;
393 }
394 }
395
396 if ( !exceptionsMatch )
397 {
398 continue;
399 }
400
401 return true;
402 }
403 }
404
405 return false;
406 }
407
408
409
410
411 public Class<?> getInterfaceClass( Class<?> clazz )
412 {
413 if ( "java.util.AbstractList$Itr".equals( clazz.getName() ) )
414 {
415 return Iterator.class;
416 }
417
418 if ( Modifier.isPublic( clazz.getModifiers() ) && clazz.isInterface() || clazz.isPrimitive() )
419 {
420 return clazz;
421 }
422
423 Class<?>[] intf = clazz.getInterfaces();
424
425 for ( Class<?> anIntf : intf )
426 {
427 if ( List.class.isAssignableFrom( anIntf ) )
428 {
429 return List.class;
430 }
431 else if ( Iterator.class.isAssignableFrom( anIntf ) )
432 {
433 return Iterator.class;
434 }
435 else if ( Map.class.isAssignableFrom( anIntf ) )
436 {
437 return Map.class;
438 }
439 else if ( Set.class.isAssignableFrom( anIntf ) )
440 {
441 return Set.class;
442 }
443 else if ( Collection.class.isAssignableFrom( anIntf ) )
444 {
445 return Collection.class;
446 }
447 }
448
449 if ( clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0 )
450 {
451 return getInterfaceClass( clazz.getSuperclass() );
452 }
453
454 return clazz;
455 }
456
457
458
459
460 public Class<?> getRootExpressionClass( Node rootNode, OgnlContext context )
461 {
462 if ( context.getRoot() == null )
463 {
464 return null;
465 }
466
467 Class<?> ret = context.getRoot().getClass();
468
469 if ( context.getFirstAccessor() != null && context.getFirstAccessor().isInstance( context.getRoot() ) )
470 {
471 ret = context.getFirstAccessor();
472 }
473
474 return ret;
475 }
476
477
478
479
480 public void compileExpression( OgnlContext context, Node expression, Object root )
481 throws Exception
482 {
483
484
485 if ( expression.getAccessor() != null )
486 {
487 return;
488 }
489
490 String getBody, setBody;
491
492 EnhancedClassLoader loader = getClassLoader( context );
493 ClassPool classPool = getClassPool( context, loader );
494
495 CtClass newClass = classPool.makeClass(
496 expression.getClass().getName() + expression.hashCode() + classCounter++ + "Accessor" );
497 newClass.addInterface( getCtClass( ExpressionAccessor.class ) );
498
499 CtClass ognlClass = getCtClass( OgnlContext.class );
500 CtClass objClass = getCtClass( Object.class );
501
502 CtMethod valueGetter = new CtMethod( objClass, "get", new CtClass[] { ognlClass, objClass }, newClass );
503 CtMethod valueSetter =
504 new CtMethod( CtClass.voidType, "set", new CtClass[] { ognlClass, objClass, objClass }, newClass );
505
506 CtField nodeMember = null;
507
508 CtClass nodeClass = getCtClass( Node.class );
509 CtMethod setExpression = null;
510
511 try
512 {
513
514 getBody = generateGetter( context, newClass, objClass, classPool, valueGetter, expression, root );
515
516 }
517 catch ( UnsupportedCompilationException uc )
518 {
519
520
521 nodeMember = new CtField( nodeClass, "_node", newClass );
522 newClass.addField( nodeMember );
523
524 getBody = generateOgnlGetter( newClass, valueGetter, nodeMember );
525
526 setExpression = CtNewMethod.setter( "setExpression", nodeMember );
527 newClass.addMethod( setExpression );
528 }
529
530 try
531 {
532
533 setBody = generateSetter( context, newClass, objClass, classPool, valueSetter, expression, root );
534
535 }
536 catch ( UnsupportedCompilationException uc )
537 {
538
539
540
541 if ( nodeMember == null )
542 {
543 nodeMember = new CtField( nodeClass, "_node", newClass );
544 newClass.addField( nodeMember );
545 }
546
547 setBody = generateOgnlSetter( newClass, valueSetter, nodeMember );
548
549 if ( setExpression == null )
550 {
551 setExpression = CtNewMethod.setter( "setExpression", nodeMember );
552 newClass.addMethod( setExpression );
553 }
554 }
555
556 try
557 {
558 newClass.addConstructor( CtNewConstructor.defaultConstructor( newClass ) );
559
560 Class<?> clazz = classPool.toClass( newClass );
561 newClass.detach();
562
563 expression.setAccessor( (ExpressionAccessor) clazz.newInstance() );
564
565
566
567 if ( nodeMember != null )
568 {
569 expression.getAccessor().setExpression( expression );
570 }
571
572 }
573 catch ( Throwable t )
574 {
575
576
577 throw new RuntimeException( "Error compiling expression on object " + root + " with expression node "
578 + expression + " getter body: " + getBody + " setter body: " + setBody, t );
579 }
580
581 }
582
583 protected String generateGetter( OgnlContext context, CtClass newClass, CtClass objClass, ClassPool classPool,
584 CtMethod valueGetter, Node expression, Object root )
585 throws Exception
586 {
587 String pre = "";
588 String post = "";
589 String body;
590
591 context.setRoot( root );
592
593
594
595
596
597 context.remove( PRE_CAST );
598
599
600
601 String getterCode = expression.toGetSourceString( context, root );
602
603 if ( getterCode == null || getterCode.trim().length() <= 0
604 && !ASTVarRef.class.isAssignableFrom( expression.getClass() ) )
605 {
606 getterCode = "null";
607 }
608
609 String castExpression = (String) context.get( PRE_CAST );
610
611 if ( context.getCurrentType() == null || context.getCurrentType().isPrimitive()
612 || Character.class.isAssignableFrom( context.getCurrentType() )
613 || Object.class == context.getCurrentType() )
614 {
615 pre = pre + " ($w) (";
616 post = post + ")";
617 }
618
619 String rootExpr = !"null".equals( getterCode ) ? getRootExpression( expression, root, context ) : "";
620
621 String noRoot = (String) context.remove( "_noRoot" );
622 if ( noRoot != null )
623 {
624 rootExpr = "";
625 }
626
627 createLocalReferences( context, classPool, newClass, objClass, valueGetter.getParameterTypes() );
628
629 if ( OrderedReturn.class.isInstance( expression )
630 && ( (OrderedReturn) expression ).getLastExpression() != null )
631 {
632 body = "{ " + ( ASTMethod.class.isInstance( expression ) || ASTChain.class.isInstance( expression )
633 ? rootExpr
634 : "" ) + ( castExpression != null ? castExpression : "" )
635 + ( (OrderedReturn) expression ).getCoreExpression() + " return " + pre
636 + ( (OrderedReturn) expression ).getLastExpression() + post + ";}";
637
638 }
639 else
640 {
641
642 body =
643 "{ return " + pre + ( castExpression != null ? castExpression : "" ) + rootExpr + getterCode + post
644 + ";}";
645 }
646
647 body = body.replaceAll( "\\.\\.", "." );
648
649
650 valueGetter.setBody( body );
651 newClass.addMethod( valueGetter );
652
653 return body;
654 }
655
656
657
658
659 public String createLocalReference( OgnlContext context, String expression, Class<?> type )
660 {
661 String referenceName = "ref" + context.incrementLocalReferenceCounter();
662 context.addLocalReference( referenceName, new LocalReferenceImpl( referenceName, expression, type ) );
663
664 String castString = "";
665 if ( !type.isPrimitive() )
666 {
667 castString = "(" + ExpressionCompiler.getCastString( type ) + ") ";
668 }
669
670 return castString + referenceName + "($$)";
671 }
672
673 void createLocalReferences( OgnlContext context, ClassPool classPool, CtClass clazz, CtClass unused,
674 CtClass[] params )
675 throws NotFoundException, CannotCompileException
676 {
677 Map<String, LocalReference> referenceMap = context.getLocalReferences();
678 if ( referenceMap == null || referenceMap.isEmpty() )
679 {
680 return;
681 }
682
683 Iterator<LocalReference> it = referenceMap.values().iterator();
684 while( it.hasNext() )
685 {
686 LocalReference ref = it.next();
687 String widener = ref.getType().isPrimitive() ? " " : " ($w) ";
688
689 String body = format( "{ return %s %s; }", widener, ref.getExpression() ).replaceAll( "\\.\\.", "." );
690
691
692
693
694 CtMethod method =
695 new CtMethod( classPool.get( getCastString( ref.getType() ) ), ref.getName(), params, clazz );
696 method.setBody( body );
697
698 clazz.addMethod( method );
699 it.remove();
700 }
701 }
702
703 protected String generateSetter( OgnlContext context, CtClass newClass, CtClass objClass, ClassPool classPool,
704 CtMethod valueSetter, Node expression, Object root )
705 throws Exception
706 {
707 if ( ExpressionNode.class.isInstance( expression ) || ASTConst.class.isInstance( expression ) )
708 {
709 throw new UnsupportedCompilationException( "Can't compile expression/constant setters." );
710 }
711
712 context.setRoot( root );
713 context.remove( PRE_CAST );
714
715 String body;
716
717 String setterCode = expression.toSetSourceString( context, root );
718 String castExpression = (String) context.get( PRE_CAST );
719
720 if ( setterCode == null || setterCode.trim().length() < 1 )
721 {
722 throw new UnsupportedCompilationException( "Can't compile null setter body." );
723 }
724
725 if ( root == null )
726 {
727 throw new UnsupportedCompilationException( "Can't compile setters with a null root object." );
728 }
729
730 String pre = getRootExpression( expression, root, context );
731
732 String noRoot = (String) context.remove( "_noRoot" );
733 if ( noRoot != null )
734 {
735 pre = "";
736 }
737
738 createLocalReferences( context, classPool, newClass, objClass, valueSetter.getParameterTypes() );
739
740 body = "{" + ( castExpression != null ? castExpression : "" ) + pre + setterCode + ";}";
741
742 body = body.replaceAll( "\\.\\.", "." );
743
744
745
746 valueSetter.setBody( body );
747 newClass.addMethod( valueSetter );
748
749 return body;
750 }
751
752
753
754
755
756
757
758
759
760
761
762 protected String generateOgnlGetter( CtClass clazz, CtMethod valueGetter, CtField node )
763 throws Exception
764 {
765 String body = "return " + node.getName() + ".getValue($1, $2);";
766
767 valueGetter.setBody( body );
768 clazz.addMethod( valueGetter );
769
770 return body;
771 }
772
773
774
775
776
777
778
779
780
781
782
783 protected String generateOgnlSetter( CtClass clazz, CtMethod valueSetter, CtField node )
784 throws Exception
785 {
786 String body = node.getName() + ".setValue($1, $2, $3);";
787
788 valueSetter.setBody( body );
789 clazz.addMethod( valueSetter );
790
791 return body;
792 }
793
794
795
796
797
798
799
800
801 protected EnhancedClassLoader getClassLoader( OgnlContext context )
802 {
803 EnhancedClassLoader ret = loaders.get( context.getClassResolver() );
804
805 if ( ret != null )
806 {
807 return ret;
808 }
809
810 ClassLoader classLoader = new ContextClassLoader( OgnlContext.class.getClassLoader(), context );
811
812 ret = new EnhancedClassLoader( classLoader );
813 loaders.put( context.getClassResolver(), ret );
814
815 return ret;
816 }
817
818
819
820
821
822
823
824
825 protected CtClass getCtClass( Class<?> searchClass )
826 throws NotFoundException
827 {
828 return pool.get( searchClass.getName() );
829 }
830
831
832
833
834
835
836
837
838
839
840
841 protected ClassPool getClassPool( OgnlContext context, EnhancedClassLoader loader )
842 {
843 if ( pool != null )
844 {
845 return pool;
846 }
847
848 pool = ClassPool.getDefault();
849 pool.insertClassPath( new LoaderClassPath( loader.getParent() ) );
850
851 return pool;
852 }
853 }