/* | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you under the Apache License, Version 2.0 (the | |
* "License"); you may not use this file except in compliance | |
* with the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, | |
* software distributed under the License is distributed on an | |
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
* KIND, either express or implied. See the License for the | |
* specific language governing permissions and limitations | |
* under the License. | |
*/ | |
package org.apache.commons.ognl.enhance; | |
import javassist.CannotCompileException; | |
import javassist.ClassPool; | |
import javassist.CtClass; | |
import javassist.CtField; | |
import javassist.CtMethod; | |
import javassist.CtNewConstructor; | |
import javassist.CtNewMethod; | |
import javassist.LoaderClassPath; | |
import javassist.NotFoundException; | |
import org.apache.commons.ognl.ASTAnd; | |
import org.apache.commons.ognl.ASTChain; | |
import org.apache.commons.ognl.ASTConst; | |
import org.apache.commons.ognl.ASTCtor; | |
import org.apache.commons.ognl.ASTList; | |
import org.apache.commons.ognl.ASTMethod; | |
import org.apache.commons.ognl.ASTOr; | |
import org.apache.commons.ognl.ASTProperty; | |
import org.apache.commons.ognl.ASTRootVarRef; | |
import org.apache.commons.ognl.ASTStaticField; | |
import org.apache.commons.ognl.ASTStaticMethod; | |
import org.apache.commons.ognl.ASTThisVarRef; | |
import org.apache.commons.ognl.ASTVarRef; | |
import org.apache.commons.ognl.ClassResolver; | |
import org.apache.commons.ognl.ExpressionNode; | |
import org.apache.commons.ognl.Node; | |
import org.apache.commons.ognl.OgnlContext; | |
import org.apache.commons.ognl.OgnlRuntime; | |
import java.lang.reflect.Method; | |
import java.lang.reflect.Modifier; | |
import java.util.Collection; | |
import java.util.HashMap; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Set; | |
import static java.lang.String.format; | |
/** | |
* Responsible for managing/providing functionality related to compiling generated java source expressions via bytecode | |
* enhancements for a given ognl expression. | |
*/ | |
public class ExpressionCompiler | |
implements OgnlExpressionCompiler | |
{ | |
/** | |
* Key used to store any java source string casting statements in the {@link OgnlContext} during class compilation. | |
*/ | |
public static final String PRE_CAST = "_preCast"; | |
/** | |
* {@link ClassLoader} instances. | |
*/ | |
protected Map<ClassResolver, EnhancedClassLoader> loaders = new HashMap<ClassResolver, EnhancedClassLoader>(); | |
/** | |
* Javassist class definition poool. | |
*/ | |
protected ClassPool pool; | |
protected int classCounter = 0; | |
/** | |
* Used by {@link #castExpression(org.apache.commons.ognl.OgnlContext, org.apache.commons.ognl.Node, String)} to | |
* store the cast java source string in to the current {@link org.apache.commons.ognl.OgnlContext}. This will either | |
* add to the existing string present if it already exists or create a new instance and store it using the static | |
* key of {@link #PRE_CAST}. | |
* | |
* @param context The current execution context. | |
* @param cast The java source string to store in to the context. | |
*/ | |
public static void addCastString( OgnlContext context, String cast ) | |
{ | |
String value = (String) context.get( PRE_CAST ); | |
if ( value != null ) | |
{ | |
value = cast + value; | |
} | |
else | |
{ | |
value = cast; | |
} | |
context.put( PRE_CAST, value ); | |
} | |
/** | |
* Returns the appropriate casting expression (minus parens) for the specified class type. | |
* <p/> | |
* For instance, if given an {@link Integer} object the string <code>"java.lang.Integer"</code> would be returned. | |
* For an array of primitive ints <code>"int[]"</code> and so on.. | |
* </p> | |
* | |
* @param type The class to cast a string expression for. | |
* @return The converted raw string version of the class name. | |
*/ | |
public static String getCastString( Class<?> type ) | |
{ | |
if ( type == null ) | |
{ | |
return null; | |
} | |
return type.isArray() ? type.getComponentType().getName() + "[]" : type.getName(); | |
} | |
/** | |
* Convenience method called by many different property/method resolving AST types to get a root expression | |
* resolving string for the given node. The callers are mostly ignorant and rely on this method to properly | |
* determine if the expression should be cast at all and take the appropriate actions if it should. | |
* | |
* @param expression The node to check and generate a root expression to if necessary. | |
* @param root The root object for this execution. | |
* @param context The current execution context. | |
* @return Either an empty string or a root path java source string compatible with javassist compilations from the | |
* root object up to the specified {@link Node}. | |
*/ | |
public static String getRootExpression( Node expression, Object root, OgnlContext context ) | |
{ | |
String rootExpr = ""; | |
if ( !shouldCast( expression ) ) | |
{ | |
return rootExpr; | |
} | |
if ( ( !(expression instanceof ASTList) && !(expression instanceof ASTVarRef) | |
&& !(expression instanceof ASTStaticMethod) && !(expression instanceof ASTStaticField) | |
&& !(expression instanceof ASTConst) && !(expression instanceof ExpressionNode) | |
&& !(expression instanceof ASTCtor) && !(expression instanceof ASTStaticMethod) | |
&& root != null ) || ( root != null && expression instanceof ASTRootVarRef) ) | |
{ | |
Class<?> castClass = OgnlRuntime.getCompiler( context ).getRootExpressionClass( expression, context ); | |
if ( castClass.isArray() || expression instanceof ASTRootVarRef || expression instanceof ASTThisVarRef) | |
{ | |
rootExpr = "((" + getCastString( castClass ) + ")$2)"; | |
if ( expression instanceof ASTProperty && !( (ASTProperty) expression ).isIndexedAccess() ) | |
{ | |
rootExpr += "."; | |
} | |
} | |
else if ( ( expression instanceof ASTProperty && ( (ASTProperty) expression ).isIndexedAccess() ) | |
|| expression instanceof ASTChain) | |
{ | |
rootExpr = "((" + getCastString( castClass ) + ")$2)"; | |
} | |
else | |
{ | |
rootExpr = "((" + getCastString( castClass ) + ")$2)."; | |
} | |
} | |
return rootExpr; | |
} | |
/** | |
* Used by {@link #getRootExpression(org.apache.commons.ognl.Node, Object, org.apache.commons.ognl.OgnlContext)} to | |
* determine if the expression needs to be cast at all. | |
* | |
* @param expression The node to check against. | |
* @return Yes if the node type should be cast - false otherwise. | |
*/ | |
public static boolean shouldCast( Node expression ) | |
{ | |
if (expression instanceof ASTChain) | |
{ | |
Node child = expression.jjtGetChild( 0 ); | |
if ( child instanceof ASTConst || child instanceof ASTStaticMethod | |
|| child instanceof ASTStaticField || ( child instanceof ASTVarRef | |
&& !(child instanceof ASTRootVarRef)) ) | |
{ | |
return false; | |
} | |
} | |
return !(expression instanceof ASTConst); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public String castExpression( OgnlContext context, Node expression, String body ) | |
{ | |
//TODO: ok - so this looks really f-ed up ...and it is ..eh if you can do it better I'm all for it :) | |
if ( context.getCurrentAccessor() == null || context.getPreviousType() == null | |
|| context.getCurrentAccessor().isAssignableFrom( context.getPreviousType() ) || ( | |
context.getCurrentType() != null && context.getCurrentObject() != null | |
&& context.getCurrentType().isAssignableFrom( context.getCurrentObject().getClass() ) | |
&& context.getCurrentAccessor().isAssignableFrom( context.getPreviousType() ) ) || body == null | |
|| body.trim().length() < 1 || ( context.getCurrentType() != null && context.getCurrentType().isArray() && ( | |
context.getPreviousType() == null || context.getPreviousType() != Object.class ) ) | |
|| expression instanceof ASTOr || expression instanceof ASTAnd | |
|| expression instanceof ASTRootVarRef || context.getCurrentAccessor() == Class.class || ( | |
context.get( ExpressionCompiler.PRE_CAST ) != null && ( (String) context.get( | |
ExpressionCompiler.PRE_CAST ) ).startsWith( "new" ) ) || expression instanceof ASTStaticField | |
|| expression instanceof ASTStaticMethod || ( expression instanceof OrderedReturn | |
&& ( (OrderedReturn) expression ).getLastExpression() != null ) ) | |
{ | |
return body; | |
} | |
/* | |
* System.out.println("castExpression() with expression " + expression + " expr class: " + expression.getClass() | |
* + " currentType is: " + context.getCurrentType() + " previousType: " + context.getPreviousType() + | |
* "\n current Accessor: " + context.getCurrentAccessor() + " previous Accessor: " + | |
* context.getPreviousAccessor() + " current object " + context.getCurrentObject()); | |
*/ | |
ExpressionCompiler.addCastString( context, | |
"((" + ExpressionCompiler.getCastString( context.getCurrentAccessor() ) | |
+ ")" ); | |
return ")" + body; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public String getClassName( Class<?> clazz ) | |
{ | |
if ( "java.util.AbstractList$Itr".equals( clazz.getName() ) ) | |
{ | |
return Iterator.class.getName(); | |
} | |
if ( Modifier.isPublic( clazz.getModifiers() ) && clazz.isInterface() ) | |
{ | |
return clazz.getName(); | |
} | |
Class<?>[] interfaces = clazz.getInterfaces(); | |
for ( Class<?> intface : interfaces ) | |
{ | |
if ( intface.getName().indexOf( "util.List" ) > 0 ) | |
{ | |
return intface.getName(); | |
} | |
if ( intface.getName().indexOf( "Iterator" ) > 0 ) | |
{ | |
return intface.getName(); | |
} | |
} | |
if ( clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0 ) | |
{ | |
return getClassName( clazz.getSuperclass() ); | |
} | |
return clazz.getName(); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public Class<?> getSuperOrInterfaceClass( Method m, Class<?> clazz ) | |
{ | |
if ( clazz.getInterfaces() != null && clazz.getInterfaces().length > 0 ) | |
{ | |
Class<?>[] intfs = clazz.getInterfaces(); | |
Class<?> intClass; | |
for ( Class<?> intf : intfs ) | |
{ | |
intClass = getSuperOrInterfaceClass( m, intf ); | |
if ( intClass != null ) | |
{ | |
return intClass; | |
} | |
if ( Modifier.isPublic( intf.getModifiers() ) && containsMethod( m, intf ) ) | |
{ | |
return intf; | |
} | |
} | |
} | |
if ( clazz.getSuperclass() != null ) | |
{ | |
Class<?> superClass = getSuperOrInterfaceClass( m, clazz.getSuperclass() ); | |
if ( superClass != null ) | |
{ | |
return superClass; | |
} | |
} | |
if ( Modifier.isPublic( clazz.getModifiers() ) && containsMethod( m, clazz ) ) | |
{ | |
return clazz; | |
} | |
return null; | |
} | |
/** | |
* Helper utility method used by compiler to help resolve class->method mappings during method calls to | |
* {@link OgnlExpressionCompiler#getSuperOrInterfaceClass(java.lang.reflect.Method, Class)}. | |
* | |
* @param m The method to check for existance of. | |
* @param clazz The class to check for the existance of a matching method definition to the method passed in. | |
* @return True if the class contains the specified method, false otherwise. | |
*/ | |
public boolean containsMethod( Method m, Class<?> clazz ) | |
{ | |
Method[] methods = clazz.getMethods(); | |
if ( methods == null ) | |
{ | |
return false; | |
} | |
for ( Method method : methods ) | |
{ | |
if ( method.getName().equals( m.getName() ) && method.getReturnType() == m.getReturnType() ) | |
{ | |
Class<?>[] parms = m.getParameterTypes(); | |
if ( parms == null ) | |
{ | |
continue; | |
} | |
Class<?>[] mparms = method.getParameterTypes(); | |
if ( mparms == null || mparms.length != parms.length ) | |
{ | |
continue; | |
} | |
boolean parmsMatch = true; | |
for ( int p = 0; p < parms.length; p++ ) | |
{ | |
if ( parms[p] != mparms[p] ) | |
{ | |
parmsMatch = false; | |
break; | |
} | |
} | |
if ( !parmsMatch ) | |
{ | |
continue; | |
} | |
Class<?>[] exceptions = m.getExceptionTypes(); | |
if ( exceptions == null ) | |
{ | |
continue; | |
} | |
Class<?>[] mexceptions = method.getExceptionTypes(); | |
if ( mexceptions == null || mexceptions.length != exceptions.length ) | |
{ | |
continue; | |
} | |
boolean exceptionsMatch = true; | |
for ( int e = 0; e < exceptions.length; e++ ) | |
{ | |
if ( exceptions[e] != mexceptions[e] ) | |
{ | |
exceptionsMatch = false; | |
break; | |
} | |
} | |
if ( !exceptionsMatch ) | |
{ | |
continue; | |
} | |
return true; | |
} | |
} | |
return false; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public Class<?> getInterfaceClass( Class<?> clazz ) | |
{ | |
if ( "java.util.AbstractList$Itr".equals( clazz.getName() ) ) | |
{ | |
return Iterator.class; | |
} | |
if ( Modifier.isPublic( clazz.getModifiers() ) && clazz.isInterface() || clazz.isPrimitive() ) | |
{ | |
return clazz; | |
} | |
Class<?>[] intf = clazz.getInterfaces(); | |
for ( Class<?> anIntf : intf ) | |
{ | |
if ( List.class.isAssignableFrom( anIntf ) ) | |
{ | |
return List.class; | |
} | |
if ( Iterator.class.isAssignableFrom( anIntf ) ) | |
{ | |
return Iterator.class; | |
} | |
if ( Map.class.isAssignableFrom( anIntf ) ) | |
{ | |
return Map.class; | |
} | |
if ( Set.class.isAssignableFrom( anIntf ) ) | |
{ | |
return Set.class; | |
} | |
if ( Collection.class.isAssignableFrom( anIntf ) ) | |
{ | |
return Collection.class; | |
} | |
} | |
if ( clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0 ) | |
{ | |
return getInterfaceClass( clazz.getSuperclass() ); | |
} | |
return clazz; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public Class<?> getRootExpressionClass( Node rootNode, OgnlContext context ) | |
{ | |
if ( context.getRoot() == null ) | |
{ | |
return null; | |
} | |
Class<?> ret = context.getRoot().getClass(); | |
if ( context.getFirstAccessor() != null && context.getFirstAccessor().isInstance( context.getRoot() ) ) | |
{ | |
ret = context.getFirstAccessor(); | |
} | |
return ret; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public void compileExpression( OgnlContext context, Node expression, Object root ) | |
throws Exception | |
{ | |
// System.out.println("Compiling expr class " + expression.getClass().getName() + " and root " + root); | |
if ( expression.getAccessor() != null ) | |
{ | |
return; | |
} | |
String getBody, setBody; | |
EnhancedClassLoader loader = getClassLoader( context ); | |
ClassPool classPool = getClassPool( context, loader ); | |
CtClass newClass = classPool.makeClass( | |
expression.getClass().getName() + expression.hashCode() + classCounter++ + "Accessor" ); | |
newClass.addInterface( getCtClass( ExpressionAccessor.class ) ); | |
CtClass ognlClass = getCtClass( OgnlContext.class ); | |
CtClass objClass = getCtClass( Object.class ); | |
CtMethod valueGetter = new CtMethod( objClass, "get", new CtClass[] { ognlClass, objClass }, newClass ); | |
CtMethod valueSetter = | |
new CtMethod( CtClass.voidType, "set", new CtClass[] { ognlClass, objClass, objClass }, newClass ); | |
CtField nodeMember = null; // will only be set if uncompilable exception is thrown | |
CtClass nodeClass = getCtClass( Node.class ); | |
CtMethod setExpression = null; | |
try | |
{ | |
getBody = generateGetter( context, newClass, objClass, classPool, valueGetter, expression, root ); | |
} | |
catch ( UnsupportedCompilationException uc ) | |
{ | |
nodeMember = new CtField( nodeClass, "_node", newClass ); | |
newClass.addField( nodeMember ); | |
getBody = generateOgnlGetter( newClass, valueGetter, nodeMember ); | |
setExpression = CtNewMethod.setter( "setExpression", nodeMember ); | |
newClass.addMethod( setExpression ); | |
} | |
try | |
{ | |
setBody = generateSetter( context, newClass, objClass, classPool, valueSetter, expression, root ); | |
} | |
catch ( UnsupportedCompilationException uc ) | |
{ | |
if ( nodeMember == null ) | |
{ | |
nodeMember = new CtField( nodeClass, "_node", newClass ); | |
newClass.addField( nodeMember ); | |
} | |
setBody = generateOgnlSetter( newClass, valueSetter, nodeMember ); | |
if ( setExpression == null ) | |
{ | |
setExpression = CtNewMethod.setter( "setExpression", nodeMember ); | |
newClass.addMethod( setExpression ); | |
} | |
} | |
try | |
{ | |
newClass.addConstructor( CtNewConstructor.defaultConstructor( newClass ) ); | |
Class<?> clazz = classPool.toClass( newClass ); | |
newClass.detach(); | |
expression.setAccessor( (ExpressionAccessor) clazz.newInstance() ); | |
// need to set expression on node if the field was just defined. | |
if ( nodeMember != null ) | |
{ | |
expression.getAccessor().setExpression( expression ); | |
} | |
} | |
catch ( Throwable t ) | |
{ | |
throw new RuntimeException( "Error compiling expression on object " + root + " with expression node " | |
+ expression + " getter body: " + getBody + " setter body: " + setBody, t ); | |
} | |
} | |
protected String generateGetter( OgnlContext context, CtClass newClass, CtClass objClass, ClassPool classPool, | |
CtMethod valueGetter, Node expression, Object root ) | |
throws Exception | |
{ | |
String pre = ""; | |
String post = ""; | |
String body; | |
context.setRoot( root ); | |
// the ExpressionAccessor API has to reference the generic Object class for get/set operations, so this sets up | |
// that known | |
// type beforehand | |
context.remove( PRE_CAST ); | |
// Recursively generate the java source code representation of the top level expression | |
String getterCode = expression.toGetSourceString( context, root ); | |
if ( getterCode == null || getterCode.trim().isEmpty() | |
&& !ASTVarRef.class.isAssignableFrom( expression.getClass() ) ) | |
{ | |
getterCode = "null"; | |
} | |
String castExpression = (String) context.get( PRE_CAST ); | |
if ( context.getCurrentType() == null || context.getCurrentType().isPrimitive() | |
|| Character.class.isAssignableFrom( context.getCurrentType() ) | |
|| Object.class == context.getCurrentType() ) | |
{ | |
pre = pre + " ($w) ("; | |
post = post + ")"; | |
} | |
String rootExpr = !"null".equals( getterCode ) ? getRootExpression( expression, root, context ) : ""; | |
String noRoot = (String) context.remove( "_noRoot" ); | |
if ( noRoot != null ) | |
{ | |
rootExpr = ""; | |
} | |
createLocalReferences( context, classPool, newClass, objClass, valueGetter.getParameterTypes() ); | |
if ( expression instanceof OrderedReturn | |
&& ( (OrderedReturn) expression ).getLastExpression() != null ) | |
{ | |
body = "{ " + ( expression instanceof ASTMethod || expression instanceof ASTChain | |
? rootExpr | |
: "" ) + ( castExpression != null ? castExpression : "" ) | |
+ ( (OrderedReturn) expression ).getCoreExpression() + " return " + pre | |
+ ( (OrderedReturn) expression ).getLastExpression() + post + ";}"; | |
} | |
else | |
{ | |
body = | |
"{ return " + pre + ( castExpression != null ? castExpression : "" ) + rootExpr + getterCode + post | |
+ ";}"; | |
} | |
body = body.replaceAll( "\\.\\.", "." ); | |
// System.out.println("Getter Body: ===================================\n" + body); | |
valueGetter.setBody( body ); | |
newClass.addMethod( valueGetter ); | |
return body; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public String createLocalReference( OgnlContext context, String expression, Class<?> type ) | |
{ | |
String referenceName = "ref" + context.incrementLocalReferenceCounter(); | |
context.addLocalReference( referenceName, new LocalReferenceImpl( referenceName, expression, type ) ); | |
String castString = ""; | |
if ( !type.isPrimitive() ) | |
{ | |
castString = "(" + ExpressionCompiler.getCastString( type ) + ") "; | |
} | |
return castString + referenceName + "($$)"; | |
} | |
void createLocalReferences( OgnlContext context, ClassPool classPool, CtClass clazz, CtClass unused, | |
CtClass[] params ) | |
throws NotFoundException, CannotCompileException | |
{ | |
Map<String, LocalReference> referenceMap = context.getLocalReferences(); | |
if ( referenceMap == null || referenceMap.isEmpty() ) | |
{ | |
return; | |
} | |
Iterator<LocalReference> it = referenceMap.values().iterator(); | |
while( it.hasNext() ) | |
{ | |
LocalReference ref = it.next(); | |
String widener = ref.getType().isPrimitive() ? " " : " ($w) "; | |
String body = format( "{ return %s %s; }", widener, ref.getExpression() ).replaceAll( "\\.\\.", "." ); | |
// System.out.println("adding method " + ref.getName() + " with body:\n" + body + " and return type: " + | |
// ref.getType()); | |
CtMethod method = | |
new CtMethod( classPool.get( getCastString( ref.getType() ) ), ref.getName(), params, clazz ); | |
method.setBody( body ); | |
clazz.addMethod( method ); | |
it.remove(); | |
} | |
} | |
protected String generateSetter( OgnlContext context, CtClass newClass, CtClass objClass, ClassPool classPool, | |
CtMethod valueSetter, Node expression, Object root ) | |
throws Exception | |
{ | |
if ( expression instanceof ExpressionNode || expression instanceof ASTConst) | |
{ | |
throw new UnsupportedCompilationException( "Can't compile expression/constant setters." ); | |
} | |
context.setRoot( root ); | |
context.remove( PRE_CAST ); | |
String body; | |
String setterCode = expression.toSetSourceString( context, root ); | |
String castExpression = (String) context.get( PRE_CAST ); | |
if ( setterCode == null || setterCode.trim().length() < 1 ) | |
{ | |
throw new UnsupportedCompilationException( "Can't compile null setter body." ); | |
} | |
if ( root == null ) | |
{ | |
throw new UnsupportedCompilationException( "Can't compile setters with a null root object." ); | |
} | |
String pre = getRootExpression( expression, root, context ); | |
String noRoot = (String) context.remove( "_noRoot" ); | |
if ( noRoot != null ) | |
{ | |
pre = ""; | |
} | |
createLocalReferences( context, classPool, newClass, objClass, valueSetter.getParameterTypes() ); | |
body = "{" + ( castExpression != null ? castExpression : "" ) + pre + setterCode + ";}"; | |
body = body.replaceAll( "\\.\\.", "." ); | |
// System.out.println("Setter Body: ===================================\n" + body); | |
valueSetter.setBody( body ); | |
newClass.addMethod( valueSetter ); | |
return body; | |
} | |
/** | |
* Fail safe getter creation when normal compilation fails. | |
* | |
* @param clazz The javassist class the new method should be attached to. | |
* @param valueGetter The method definition the generated code will be contained within. | |
* @param node The root expression node. | |
* @return The generated source string for this method, the method will still be added via the javassist API either | |
* way so this is really a convenience for exception reporting / debugging. | |
* @throws Exception If a javassist error occurs. | |
*/ | |
protected String generateOgnlGetter( CtClass clazz, CtMethod valueGetter, CtField node ) | |
throws Exception | |
{ | |
String body = "return " + node.getName() + ".getValue($1, $2);"; | |
valueGetter.setBody( body ); | |
clazz.addMethod( valueGetter ); | |
return body; | |
} | |
/** | |
* Fail safe setter creation when normal compilation fails. | |
* | |
* @param clazz The javassist class the new method should be attached to. | |
* @param valueSetter The method definition the generated code will be contained within. | |
* @param node The root expression node. | |
* @return The generated source string for this method, the method will still be added via the javassist API either | |
* way so this is really a convenience for exception reporting / debugging. | |
* @throws Exception If a javassist error occurs. | |
*/ | |
protected String generateOgnlSetter( CtClass clazz, CtMethod valueSetter, CtField node ) | |
throws Exception | |
{ | |
String body = node.getName() + ".setValue($1, $2, $3);"; | |
valueSetter.setBody( body ); | |
clazz.addMethod( valueSetter ); | |
return body; | |
} | |
/** | |
* Creates a {@link ClassLoader} instance compatible with the javassist classloader and normal OGNL class resolving | |
* semantics. | |
* | |
* @param context The current execution context. | |
* @return The created {@link ClassLoader} instance. | |
*/ | |
protected EnhancedClassLoader getClassLoader( OgnlContext context ) | |
{ | |
EnhancedClassLoader ret = loaders.get( context.getClassResolver() ); | |
if ( ret != null ) | |
{ | |
return ret; | |
} | |
ClassLoader classLoader = new ContextClassLoader( OgnlContext.class.getClassLoader(), context ); | |
ret = new EnhancedClassLoader( classLoader ); | |
loaders.put( context.getClassResolver(), ret ); | |
return ret; | |
} | |
/** | |
* Loads a new class definition via javassist for the specified class. | |
* | |
* @param searchClass The class to load. | |
* @return The javassist class equivalent. | |
* @throws javassist.NotFoundException When the class definition can't be found. | |
*/ | |
protected CtClass getCtClass( Class<?> searchClass ) | |
throws NotFoundException | |
{ | |
return pool.get( searchClass.getName() ); | |
} | |
/** | |
* Gets either a new or existing {@link ClassPool} for use in compiling javassist classes. A new class path object | |
* is inserted in to the returned {@link ClassPool} using the passed in <code>loader</code> instance if a new pool | |
* needs to be created. | |
* | |
* @param context The current execution context. | |
* @param loader The {@link ClassLoader} instance to use - as returned by | |
* {@link #getClassLoader(org.apache.commons.ognl.OgnlContext)}. | |
* @return The existing or new {@link ClassPool} instance. | |
*/ | |
protected ClassPool getClassPool( OgnlContext context, EnhancedClassLoader loader ) | |
{ | |
if ( pool != null ) | |
{ | |
return pool; | |
} | |
pool = ClassPool.getDefault(); | |
pool.insertClassPath( new LoaderClassPath( loader.getParent() ) ); | |
return pool; | |
} | |
} |