View Javadoc

1   package org.apache.commons.ognl.enhance;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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   * Responsible for managing/providing functionality related to compiling generated java source expressions via bytecode
63   * enhancements for a given ognl expression.
64   */
65  public class ExpressionCompiler
66      implements OgnlExpressionCompiler
67  {
68  
69      /**
70       * Key used to store any java source string casting statements in the {@link OgnlContext} during class compilation.
71       */
72      public static final String PRE_CAST = "_preCast";
73  
74      /**
75       * {@link ClassLoader} instances.
76       */
77      protected Map<ClassResolver, EnhancedClassLoader> loaders = new HashMap<ClassResolver, EnhancedClassLoader>();
78  
79      /**
80       * Javassist class definition poool.
81       */
82      protected ClassPool pool;
83  
84      protected int classCounter = 0;
85  
86      /**
87       * Used by {@link #castExpression(org.apache.commons.ognl.OgnlContext, org.apache.commons.ognl.Node, String)} to
88       * store the cast java source string in to the current {@link org.apache.commons.ognl.OgnlContext}. This will either
89       * add to the existing string present if it already exists or create a new instance and store it using the static
90       * key of {@link #PRE_CAST}.
91       * 
92       * @param context The current execution context.
93       * @param cast The java source string to store in to the context.
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      * 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 }