001 package org.apache.commons.ognl.enhance;
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 javassist.CannotCompileException;
023 import javassist.ClassPool;
024 import javassist.CtClass;
025 import javassist.CtField;
026 import javassist.CtMethod;
027 import javassist.CtNewConstructor;
028 import javassist.CtNewMethod;
029 import javassist.LoaderClassPath;
030 import javassist.NotFoundException;
031 import org.apache.commons.ognl.ASTAnd;
032 import org.apache.commons.ognl.ASTChain;
033 import org.apache.commons.ognl.ASTConst;
034 import org.apache.commons.ognl.ASTCtor;
035 import org.apache.commons.ognl.ASTList;
036 import org.apache.commons.ognl.ASTMethod;
037 import org.apache.commons.ognl.ASTOr;
038 import org.apache.commons.ognl.ASTProperty;
039 import org.apache.commons.ognl.ASTRootVarRef;
040 import org.apache.commons.ognl.ASTStaticField;
041 import org.apache.commons.ognl.ASTStaticMethod;
042 import org.apache.commons.ognl.ASTThisVarRef;
043 import org.apache.commons.ognl.ASTVarRef;
044 import org.apache.commons.ognl.ClassResolver;
045 import org.apache.commons.ognl.ExpressionNode;
046 import org.apache.commons.ognl.Node;
047 import org.apache.commons.ognl.OgnlContext;
048 import org.apache.commons.ognl.OgnlRuntime;
049
050 import java.lang.reflect.Method;
051 import java.lang.reflect.Modifier;
052 import java.util.Collection;
053 import java.util.HashMap;
054 import java.util.Iterator;
055 import java.util.List;
056 import java.util.Map;
057 import java.util.Set;
058
059 import static java.lang.String.format;
060
061 /**
062 * Responsible for managing/providing functionality related to compiling generated java source expressions via bytecode
063 * enhancements for a given ognl expression.
064 */
065 public class ExpressionCompiler
066 implements OgnlExpressionCompiler
067 {
068
069 /**
070 * Key used to store any java source string casting statements in the {@link OgnlContext} during class compilation.
071 */
072 public static final String PRE_CAST = "_preCast";
073
074 /**
075 * {@link ClassLoader} instances.
076 */
077 protected Map<ClassResolver, EnhancedClassLoader> loaders = new HashMap<ClassResolver, EnhancedClassLoader>();
078
079 /**
080 * Javassist class definition poool.
081 */
082 protected ClassPool pool;
083
084 protected int classCounter = 0;
085
086 /**
087 * Used by {@link #castExpression(org.apache.commons.ognl.OgnlContext, org.apache.commons.ognl.Node, String)} to
088 * store the cast java source string in to the current {@link org.apache.commons.ognl.OgnlContext}. This will either
089 * add to the existing string present if it already exists or create a new instance and store it using the static
090 * key of {@link #PRE_CAST}.
091 *
092 * @param context The current execution context.
093 * @param cast The java source string to store in to the context.
094 */
095 public static void addCastString( OgnlContext context, String cast )
096 {
097 String value = (String) context.get( PRE_CAST );
098
099 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 * Returns the appropriate casting expression (minus parens) for the specified class type.
113 * <p/>
114 * For instance, if given an {@link Integer} object the string <code>"java.lang.Integer"</code> would be returned.
115 * For an array of primitive ints <code>"int[]"</code> and so on..
116 * </p>
117 *
118 * @param type The class to cast a string expression for.
119 * @return The converted raw string version of the class name.
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 * Convenience method called by many different property/method resolving AST types to get a root expression
133 * resolving string for the given node. The callers are mostly ignorant and rely on this method to properly
134 * determine if the expression should be cast at all and take the appropriate actions if it should.
135 *
136 * @param expression The node to check and generate a root expression to if necessary.
137 * @param root The root object for this execution.
138 * @param context The current execution context.
139 * @return Either an empty string or a root path java source string compatible with javassist compilations from the
140 * root object up to the specified {@link Node}.
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 * Used by {@link #getRootExpression(org.apache.commons.ognl.Node, Object, org.apache.commons.ognl.OgnlContext)} to
186 * determine if the expression needs to be cast at all.
187 *
188 * @param expression The node to check against.
189 * @return Yes if the node type should be cast - false otherwise.
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 * {@inheritDoc}
209 */
210 public String castExpression( OgnlContext context, Node expression, String body )
211 {
212 //TODO: ok - so this looks really f-ed up ...and it is ..eh if you can do it better I'm all for it :)
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 * System.out.println("castExpression() with expression " + expression + " expr class: " + expression.getClass()
233 * + " currentType is: " + context.getCurrentType() + " previousType: " + context.getPreviousType() +
234 * "\n current Accessor: " + context.getCurrentAccessor() + " previous Accessor: " +
235 * context.getPreviousAccessor() + " current object " + context.getCurrentObject());
236 */
237
238 ExpressionCompiler.addCastString( context,
239 "((" + ExpressionCompiler.getCastString( context.getCurrentAccessor() )
240 + ")" );
241
242 return ")" + body;
243 }
244
245 /**
246 * {@inheritDoc}
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 * {@inheritDoc}
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 * Helper utility method used by compiler to help resolve class->method mappings during method calls to
328 * {@link OgnlExpressionCompiler#getSuperOrInterfaceClass(java.lang.reflect.Method, Class)}.
329 *
330 * @param m The method to check for existance of.
331 * @param clazz The class to check for the existance of a matching method definition to the method passed in.
332 * @return True if the class contains the specified method, false otherwise.
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 * {@inheritDoc}
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 * {@inheritDoc}
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 * {@inheritDoc}
479 */
480 public void compileExpression( OgnlContext context, Node expression, Object root )
481 throws Exception
482 {
483 // System.out.println("Compiling expr class " + expression.getClass().getName() + " and root " + root);
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; // will only be set if uncompilable exception is thrown
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 // uc.printStackTrace();
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 // uc.printStackTrace();
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 // need to set expression on node if the field was just defined.
566
567 if ( nodeMember != null )
568 {
569 expression.getAccessor().setExpression( expression );
570 }
571
572 }
573 catch ( Throwable t )
574 {
575 // t.printStackTrace();
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 // the ExpressionAccessor API has to reference the generic Object class for get/set operations, so this sets up
594 // that known
595 // type beforehand
596
597 context.remove( PRE_CAST );
598
599 // Recursively generate the java source code representation of the top level expression
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 // System.out.println("Getter Body: ===================================\n" + body);
650 valueGetter.setBody( body );
651 newClass.addMethod( valueGetter );
652
653 return body;
654 }
655
656 /**
657 * {@inheritDoc}
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 // System.out.println("adding method " + ref.getName() + " with body:\n" + body + " and return type: " +
692 // ref.getType());
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 // System.out.println("Setter Body: ===================================\n" + body);
745
746 valueSetter.setBody( body );
747 newClass.addMethod( valueSetter );
748
749 return body;
750 }
751
752 /**
753 * Fail safe getter creation when normal compilation fails.
754 *
755 * @param clazz The javassist class the new method should be attached to.
756 * @param valueGetter The method definition the generated code will be contained within.
757 * @param node The root expression node.
758 * @return The generated source string for this method, the method will still be added via the javassist API either
759 * way so this is really a convenience for exception reporting / debugging.
760 * @throws Exception If a javassist error occurs.
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 * Fail safe setter creation when normal compilation fails.
775 *
776 * @param clazz The javassist class the new method should be attached to.
777 * @param valueSetter The method definition the generated code will be contained within.
778 * @param node The root expression node.
779 * @return The generated source string for this method, the method will still be added via the javassist API either
780 * way so this is really a convenience for exception reporting / debugging.
781 * @throws Exception If a javassist error occurs.
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 * Creates a {@link ClassLoader} instance compatible with the javassist classloader and normal OGNL class resolving
796 * semantics.
797 *
798 * @param context The current execution context.
799 * @return The created {@link ClassLoader} instance.
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 * Loads a new class definition via javassist for the specified class.
820 *
821 * @param searchClass The class to load.
822 * @return The javassist class equivalent.
823 * @throws javassist.NotFoundException When the class definition can't be found.
824 */
825 protected CtClass getCtClass( Class<?> searchClass )
826 throws NotFoundException
827 {
828 return pool.get( searchClass.getName() );
829 }
830
831 /**
832 * Gets either a new or existing {@link ClassPool} for use in compiling javassist classes. A new class path object
833 * is inserted in to the returned {@link ClassPool} using the passed in <code>loader</code> instance if a new pool
834 * needs to be created.
835 *
836 * @param context The current execution context.
837 * @param loader The {@link ClassLoader} instance to use - as returned by
838 * {@link #getClassLoader(org.apache.commons.ognl.OgnlContext)}.
839 * @return The existing or new {@link ClassPool} instance.
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 }