| package org.apache.commons.ognl.enhance; |
| |
| /* |
| * 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. |
| */ |
| |
| 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 ( ( !ASTList.class.isInstance( expression ) && !ASTVarRef.class.isInstance( expression ) |
| && !ASTStaticMethod.class.isInstance( expression ) && !ASTStaticField.class.isInstance( expression ) |
| && !ASTConst.class.isInstance( expression ) && !ExpressionNode.class.isInstance( expression ) |
| && !ASTCtor.class.isInstance( expression ) && !ASTStaticMethod.class.isInstance( expression ) |
| && root != null ) || ( root != null && ASTRootVarRef.class.isInstance( expression ) ) ) |
| { |
| |
| Class<?> castClass = OgnlRuntime.getCompiler( context ).getRootExpressionClass( expression, context ); |
| |
| if ( castClass.isArray() || ASTRootVarRef.class.isInstance( expression ) || ASTThisVarRef.class.isInstance( |
| expression ) ) |
| { |
| rootExpr = "((" + getCastString( castClass ) + ")$2)"; |
| |
| if ( ASTProperty.class.isInstance( expression ) && !( (ASTProperty) expression ).isIndexedAccess() ) |
| { |
| rootExpr += "."; |
| } |
| } |
| else if ( ( ASTProperty.class.isInstance( expression ) && ( (ASTProperty) expression ).isIndexedAccess() ) |
| || ASTChain.class.isInstance( expression ) ) |
| { |
| 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 ( ASTChain.class.isInstance( expression ) ) |
| { |
| Node child = expression.jjtGetChild( 0 ); |
| if ( ASTConst.class.isInstance( child ) || ASTStaticMethod.class.isInstance( child ) |
| || ASTStaticField.class.isInstance( child ) || ( ASTVarRef.class.isInstance( child ) |
| && !ASTRootVarRef.class.isInstance( child ) ) ) |
| { |
| return false; |
| } |
| } |
| |
| return !ASTConst.class.isInstance( expression ); |
| } |
| |
| /** |
| * {@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 ) ) |
| || ASTOr.class.isInstance( expression ) || ASTAnd.class.isInstance( expression ) |
| || ASTRootVarRef.class.isInstance( expression ) || context.getCurrentAccessor() == Class.class || ( |
| context.get( ExpressionCompiler.PRE_CAST ) != null && ( (String) context.get( |
| ExpressionCompiler.PRE_CAST ) ).startsWith( "new" ) ) || ASTStaticField.class.isInstance( expression ) |
| || ASTStaticMethod.class.isInstance( expression ) || ( OrderedReturn.class.isInstance( expression ) |
| && ( (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(); |
| } |
| else 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; |
| } |
| else if ( Iterator.class.isAssignableFrom( anIntf ) ) |
| { |
| return Iterator.class; |
| } |
| else if ( Map.class.isAssignableFrom( anIntf ) ) |
| { |
| return Map.class; |
| } |
| else if ( Set.class.isAssignableFrom( anIntf ) ) |
| { |
| return Set.class; |
| } |
| else 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().length() <= 0 |
| && !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 ( OrderedReturn.class.isInstance( expression ) |
| && ( (OrderedReturn) expression ).getLastExpression() != null ) |
| { |
| body = "{ " + ( ASTMethod.class.isInstance( expression ) || ASTChain.class.isInstance( expression ) |
| ? 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 ( ExpressionNode.class.isInstance( expression ) || ASTConst.class.isInstance( expression ) ) |
| { |
| 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; |
| } |
| } |