This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-ognl.git
The following commit(s) were added to refs/heads/master by this push: new 1d2048d Move Apache license header to the top of the file 1d2048d is described below commit 1d2048db25b79b3f53b969f5b3971e0d7c17d797 Author: Gary Gregory <ggreg...@rocketsoftware.com> AuthorDate: Tue Aug 30 10:52:47 2022 -0400 Move Apache license header to the top of the file --- .../commons/ognl/enhance/ContextClassLoader.java | 114 +- .../commons/ognl/enhance/EnhancedClassLoader.java | 84 +- .../commons/ognl/enhance/ExpressionAccessor.java | 116 +- .../commons/ognl/enhance/ExpressionCompiler.java | 1690 ++++++++++---------- .../commons/ognl/enhance/LocalReference.java | 96 +- .../commons/ognl/enhance/LocalReferenceImpl.java | 242 +-- .../ognl/enhance/OgnlExpressionCompiler.java | 250 +-- .../apache/commons/ognl/enhance/OrderedReturn.java | 87 +- .../enhance/UnsupportedCompilationException.java | 96 +- .../apache/commons/ognl/enhance/package-info.java | 48 +- 10 files changed, 1411 insertions(+), 1412 deletions(-) diff --git a/src/main/java/org/apache/commons/ognl/enhance/ContextClassLoader.java b/src/main/java/org/apache/commons/ognl/enhance/ContextClassLoader.java index 1c5d765..f8814f1 100644 --- a/src/main/java/org/apache/commons/ognl/enhance/ContextClassLoader.java +++ b/src/main/java/org/apache/commons/ognl/enhance/ContextClassLoader.java @@ -1,57 +1,57 @@ -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 org.apache.commons.ognl.OgnlContext; - -public class ContextClassLoader - extends ClassLoader -{ - private final OgnlContext context; - - /* - * =================================================================== Constructors - * =================================================================== - */ - public ContextClassLoader( ClassLoader parentClassLoader, OgnlContext context ) - { - super( parentClassLoader ); - this.context = context; - } - - /* - * =================================================================== Overridden methods - * =================================================================== - */ - /** - * {@inheritDoc} - */ - @Override - protected Class<?> findClass( String name ) - throws ClassNotFoundException - { - if ( ( context != null ) && ( context.getClassResolver() != null ) ) - { - return context.getClassResolver().classForName( name, context ); - } - return super.findClass( name ); - } - -} +/* + * 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 org.apache.commons.ognl.OgnlContext; + +public class ContextClassLoader + extends ClassLoader +{ + private final OgnlContext context; + + /* + * =================================================================== Constructors + * =================================================================== + */ + public ContextClassLoader( ClassLoader parentClassLoader, OgnlContext context ) + { + super( parentClassLoader ); + this.context = context; + } + + /* + * =================================================================== Overridden methods + * =================================================================== + */ + /** + * {@inheritDoc} + */ + @Override + protected Class<?> findClass( String name ) + throws ClassNotFoundException + { + if ( ( context != null ) && ( context.getClassResolver() != null ) ) + { + return context.getClassResolver().classForName( name, context ); + } + return super.findClass( name ); + } + +} diff --git a/src/main/java/org/apache/commons/ognl/enhance/EnhancedClassLoader.java b/src/main/java/org/apache/commons/ognl/enhance/EnhancedClassLoader.java index 84cb8db..8470a82 100644 --- a/src/main/java/org/apache/commons/ognl/enhance/EnhancedClassLoader.java +++ b/src/main/java/org/apache/commons/ognl/enhance/EnhancedClassLoader.java @@ -1,42 +1,42 @@ -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. - */ - -public class EnhancedClassLoader - extends ClassLoader -{ - /* - * =================================================================== Constructors - * =================================================================== - */ - public EnhancedClassLoader( ClassLoader parentClassLoader ) - { - super( parentClassLoader ); - } - - /* - * =================================================================== Overridden methods - * =================================================================== - */ - public Class<?> defineClass( String enhancedClassName, byte[] byteCode ) - { - return defineClass( enhancedClassName, byteCode, 0, byteCode.length ); - } -} +/* + * 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; + +public class EnhancedClassLoader + extends ClassLoader +{ + /* + * =================================================================== Constructors + * =================================================================== + */ + public EnhancedClassLoader( ClassLoader parentClassLoader ) + { + super( parentClassLoader ); + } + + /* + * =================================================================== Overridden methods + * =================================================================== + */ + public Class<?> defineClass( String enhancedClassName, byte[] byteCode ) + { + return defineClass( enhancedClassName, byteCode, 0, byteCode.length ); + } +} diff --git a/src/main/java/org/apache/commons/ognl/enhance/ExpressionAccessor.java b/src/main/java/org/apache/commons/ognl/enhance/ExpressionAccessor.java index 7b51efc..6905516 100644 --- a/src/main/java/org/apache/commons/ognl/enhance/ExpressionAccessor.java +++ b/src/main/java/org/apache/commons/ognl/enhance/ExpressionAccessor.java @@ -1,58 +1,58 @@ -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 org.apache.commons.ognl.Node; -import org.apache.commons.ognl.OgnlContext; - -/** - * Provides pure java expression paths to get/set values from an ognl expression. This is achieved by taking an existing - * {@link Node} parsed expression and using bytecode enhancements to do the same work using pure java vs the ognl - * interpreter. - */ -public interface ExpressionAccessor -{ - - /** - * Gets the value represented by this expression path, if any. - * - * @param context The standard ognl context used for variable substitution/etc. - * @param target The root object this expression is meant for. - * @return The evaluated value, if any. - */ - Object get( OgnlContext context, Object target ); - - /** - * Sets the value represented by this expression path, if possible. - * - * @param context The standard ognl context used for variable substitution/etc. - * @param target The root object this expression is meant for. - * @param value The new value to set if this expression references a settable property. - */ - void set( OgnlContext context, Object target, Object value ); - - /** - * Used to set the original root expression node on instances where the compiled version has to fall back to - * interpreted syntax because of compilation failures. - * - * @param expression The root expression node used to generate this accessor. - */ - void setExpression( Node expression ); -} +/* + * 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 org.apache.commons.ognl.Node; +import org.apache.commons.ognl.OgnlContext; + +/** + * Provides pure java expression paths to get/set values from an ognl expression. This is achieved by taking an existing + * {@link Node} parsed expression and using bytecode enhancements to do the same work using pure java vs the ognl + * interpreter. + */ +public interface ExpressionAccessor +{ + + /** + * Gets the value represented by this expression path, if any. + * + * @param context The standard ognl context used for variable substitution/etc. + * @param target The root object this expression is meant for. + * @return The evaluated value, if any. + */ + Object get( OgnlContext context, Object target ); + + /** + * Sets the value represented by this expression path, if possible. + * + * @param context The standard ognl context used for variable substitution/etc. + * @param target The root object this expression is meant for. + * @param value The new value to set if this expression references a settable property. + */ + void set( OgnlContext context, Object target, Object value ); + + /** + * Used to set the original root expression node on instances where the compiled version has to fall back to + * interpreted syntax because of compilation failures. + * + * @param expression The root expression node used to generate this accessor. + */ + void setExpression( Node expression ); +} diff --git a/src/main/java/org/apache/commons/ognl/enhance/ExpressionCompiler.java b/src/main/java/org/apache/commons/ognl/enhance/ExpressionCompiler.java index 628af40..e75eadc 100644 --- a/src/main/java/org/apache/commons/ognl/enhance/ExpressionCompiler.java +++ b/src/main/java/org/apache/commons/ognl/enhance/ExpressionCompiler.java @@ -1,845 +1,845 @@ -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 ( ( !(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; - } -} +/* + * 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; + } +} diff --git a/src/main/java/org/apache/commons/ognl/enhance/LocalReference.java b/src/main/java/org/apache/commons/ognl/enhance/LocalReference.java index 57a9be3..a9c18cd 100644 --- a/src/main/java/org/apache/commons/ognl/enhance/LocalReference.java +++ b/src/main/java/org/apache/commons/ognl/enhance/LocalReference.java @@ -1,48 +1,48 @@ -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. - */ - -/** - * Container class for {@link OgnlExpressionCompiler} generated local method block references. - */ -public interface LocalReference -{ - - /** - * The name of the assigned variable reference. - * - * @return The name of the reference as it will be when compiled. - */ - String getName(); - - /** - * The expression that sets the value, ie the part after <code><class type> refName = <expression></code>. - * - * @return The setting expression. - */ - String getExpression(); - - /** - * The type of reference. - * - * @return The type. - */ - Class<?> getType(); -} +/* + * 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; + +/** + * Container class for {@link OgnlExpressionCompiler} generated local method block references. + */ +public interface LocalReference +{ + + /** + * The name of the assigned variable reference. + * + * @return The name of the reference as it will be when compiled. + */ + String getName(); + + /** + * The expression that sets the value, ie the part after <code><class type> refName = <expression></code>. + * + * @return The setting expression. + */ + String getExpression(); + + /** + * The type of reference. + * + * @return The type. + */ + Class<?> getType(); +} diff --git a/src/main/java/org/apache/commons/ognl/enhance/LocalReferenceImpl.java b/src/main/java/org/apache/commons/ognl/enhance/LocalReferenceImpl.java index 0ebddd3..cb078bb 100644 --- a/src/main/java/org/apache/commons/ognl/enhance/LocalReferenceImpl.java +++ b/src/main/java/org/apache/commons/ognl/enhance/LocalReferenceImpl.java @@ -1,121 +1,121 @@ -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. - */ - -/** - * Implementation of {@link LocalReference}. - */ -public class LocalReferenceImpl - implements LocalReference -{ - - private final String name; - - private final Class<?> type; - - private final String expression; - - public LocalReferenceImpl( String name, String expression, Class<?> type ) - { - this.name = name; - this.type = type; - this.expression = expression; - } - - /** - * {@inheritDoc} - */ - public String getName() - { - return name; - } - - /** - * {@inheritDoc} - */ - public String getExpression() - { - return expression; - } - - /** - * {@inheritDoc} - */ - public Class<?> getType() - { - return type; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals( Object o ) - { - if ( this == o ) - { - return true; - } - if ( o == null || getClass() != o.getClass() ) - { - return false; - } - - LocalReferenceImpl that = (LocalReferenceImpl) o; - - if ( expression != null ? !expression.equals( that.expression ) : that.expression != null ) - { - return false; - } - if ( name != null ? !name.equals( that.name ) : that.name != null ) - { - return false; - } - if ( type != null ? !type.equals( that.type ) : that.type != null ) - { - return false; - } - - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() - { - int result; - result = ( name != null ? name.hashCode() : 0 ); - result = 31 * result + ( type != null ? type.hashCode() : 0 ); - result = 31 * result + ( expression != null ? expression.hashCode() : 0 ); - return result; - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() - { - return "LocalReferenceImpl[" + "_name='" + name + '\'' + '\n' + ", _type=" + type + '\n' + ", _expression='" - + expression + '\'' + '\n' + ']'; - } -} +/* + * 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; + +/** + * Implementation of {@link LocalReference}. + */ +public class LocalReferenceImpl + implements LocalReference +{ + + private final String name; + + private final Class<?> type; + + private final String expression; + + public LocalReferenceImpl( String name, String expression, Class<?> type ) + { + this.name = name; + this.type = type; + this.expression = expression; + } + + /** + * {@inheritDoc} + */ + public String getName() + { + return name; + } + + /** + * {@inheritDoc} + */ + public String getExpression() + { + return expression; + } + + /** + * {@inheritDoc} + */ + public Class<?> getType() + { + return type; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals( Object o ) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + + LocalReferenceImpl that = (LocalReferenceImpl) o; + + if ( expression != null ? !expression.equals( that.expression ) : that.expression != null ) + { + return false; + } + if ( name != null ? !name.equals( that.name ) : that.name != null ) + { + return false; + } + if ( type != null ? !type.equals( that.type ) : that.type != null ) + { + return false; + } + + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() + { + int result; + result = ( name != null ? name.hashCode() : 0 ); + result = 31 * result + ( type != null ? type.hashCode() : 0 ); + result = 31 * result + ( expression != null ? expression.hashCode() : 0 ); + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() + { + return "LocalReferenceImpl[" + "_name='" + name + '\'' + '\n' + ", _type=" + type + '\n' + ", _expression='" + + expression + '\'' + '\n' + ']'; + } +} diff --git a/src/main/java/org/apache/commons/ognl/enhance/OgnlExpressionCompiler.java b/src/main/java/org/apache/commons/ognl/enhance/OgnlExpressionCompiler.java index 3ef9255..55b1421 100644 --- a/src/main/java/org/apache/commons/ognl/enhance/OgnlExpressionCompiler.java +++ b/src/main/java/org/apache/commons/ognl/enhance/OgnlExpressionCompiler.java @@ -1,125 +1,125 @@ -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 org.apache.commons.ognl.Node; -import org.apache.commons.ognl.OgnlContext; - -import java.lang.reflect.Method; - -/** - * Core interface implemented by expression compiler instances. - */ -public interface OgnlExpressionCompiler -{ - - /** Static constant used in conjunction with {@link OgnlContext} to store temporary references. */ - String ROOT_TYPE = "-ognl-root-type"; - - /** - * The core method executed to compile a specific expression. It is expected that this expression always return a - * {@link Node} with a non null {@link org.apache.commons.ognl.Node#getAccessor()} instance - unless an exception is - * thrown by the method or the statement wasn't compilable in this instance because of missing/null objects in the - * expression. These instances may in some cases continue to call this compilation method until the expression is - * resolvable. - * - * @param context The context of execution. - * @param expression The pre-parsed root expression node to compile. - * @param root The root object for the expression - may be null in many instances so some implementations may exit - * @throws Exception If an error occurs compiling the expression and no strategy has been implemented to handle - * incremental expression compilation for incomplete expression members. - */ - void compileExpression( OgnlContext context, Node expression, Object root ) - throws Exception; - - /** - * Gets a javassist safe class string for the given class instance. This is especially useful for handling array vs. - * normal class casting strings. - * - * @param clazz The class to get a string equivalent javassist compatible string reference for. - * @return The string equivalent of the class. - */ - String getClassName( Class<?> clazz ); - - /** - * Used in places where the preferred {@link #getSuperOrInterfaceClass(java.lang.reflect.Method, Class)} isn't - * possible because the method isn't known for a class. Attempts to upcast the given class to the next available - * non-private accessible class so that compiled expressions can reference the interface class of an instance so as - * not to be compiled in to overly specific statements. - * - * @param clazz The class to attempt to find a compatible interface for. - * @return The same class if no higher level interface could be matched against or the interface equivalent class. - */ - Class<?> getInterfaceClass( Class<?> clazz ); - - /** - * For the given {@link Method} and class finds the highest level interface class this combination can be cast to. - * - * @param m The method the class must implement. - * @param clazz The current class being worked with. - * @return The highest level interface / class that the referenced {@link Method} is declared in. - */ - Class<?> getSuperOrInterfaceClass( Method m, Class<?> clazz ); - - /** - * For a given root object type returns the base class type to be used in root referenced expressions. This helps in - * some instances where the root objects themselves are compiled javassist instances that need more generic class - * equivalents to cast to. - * - * @param rootNode The root expression node. - * @param context The current execution context. - * @return The root expression class type to cast to for this node. - */ - Class<?> getRootExpressionClass( Node rootNode, OgnlContext context ); - - /** - * Used primarily by AST types like {@link org.apache.commons.ognl.ASTChain} where <code>foo.bar.id</code> type - * references may need to be cast multiple times in order to properly resolve the members in a compiled statement. - * <p> - * This method should be using the various {@link org.apache.commons.ognl.OgnlContext#getCurrentType()} / - * {@link org.apache.commons.ognl.OgnlContext#getCurrentAccessor()} methods to inspect the type stack and properly - * cast to the right classes - but only when necessary. - * </p> - * - * @param context The current execution context. - * @param expression The node being checked for casting. - * @param body The java source string generated by the given node. - * @return The body string parameter plus any additional casting syntax needed to make the expression resolvable. - */ - String castExpression( OgnlContext context, Node expression, String body ); - - /** - * Method is used for expressions where multiple inner parameter method calls in generated java source strings cause - * javassit failures. It is hacky and cumbersome to have to generate expressions this way but it's the only current - * known way to make javassist happy. - * <p> - * Takes an expression block generated by a node and creates a new method on the base object being compiled so that - * sufficiently complicated sub expression blocks can be broken out in to distinct methods to be referenced by the - * core accessor / setter methods in the base compiled root object. - * </p> - * - * @param context The current execution context. - * @param expression The java source expression to dump in to a seperate method reference. - * @param type The return type that should be specified for the new method. - * @return The method name that will be used to reference the sub expression in place of the actual sub expression - * itself. - */ - String createLocalReference( OgnlContext context, String expression, Class<?> type ); -} +/* + * 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 org.apache.commons.ognl.Node; +import org.apache.commons.ognl.OgnlContext; + +import java.lang.reflect.Method; + +/** + * Core interface implemented by expression compiler instances. + */ +public interface OgnlExpressionCompiler +{ + + /** Static constant used in conjunction with {@link OgnlContext} to store temporary references. */ + String ROOT_TYPE = "-ognl-root-type"; + + /** + * The core method executed to compile a specific expression. It is expected that this expression always return a + * {@link Node} with a non null {@link org.apache.commons.ognl.Node#getAccessor()} instance - unless an exception is + * thrown by the method or the statement wasn't compilable in this instance because of missing/null objects in the + * expression. These instances may in some cases continue to call this compilation method until the expression is + * resolvable. + * + * @param context The context of execution. + * @param expression The pre-parsed root expression node to compile. + * @param root The root object for the expression - may be null in many instances so some implementations may exit + * @throws Exception If an error occurs compiling the expression and no strategy has been implemented to handle + * incremental expression compilation for incomplete expression members. + */ + void compileExpression( OgnlContext context, Node expression, Object root ) + throws Exception; + + /** + * Gets a javassist safe class string for the given class instance. This is especially useful for handling array vs. + * normal class casting strings. + * + * @param clazz The class to get a string equivalent javassist compatible string reference for. + * @return The string equivalent of the class. + */ + String getClassName( Class<?> clazz ); + + /** + * Used in places where the preferred {@link #getSuperOrInterfaceClass(java.lang.reflect.Method, Class)} isn't + * possible because the method isn't known for a class. Attempts to upcast the given class to the next available + * non-private accessible class so that compiled expressions can reference the interface class of an instance so as + * not to be compiled in to overly specific statements. + * + * @param clazz The class to attempt to find a compatible interface for. + * @return The same class if no higher level interface could be matched against or the interface equivalent class. + */ + Class<?> getInterfaceClass( Class<?> clazz ); + + /** + * For the given {@link Method} and class finds the highest level interface class this combination can be cast to. + * + * @param m The method the class must implement. + * @param clazz The current class being worked with. + * @return The highest level interface / class that the referenced {@link Method} is declared in. + */ + Class<?> getSuperOrInterfaceClass( Method m, Class<?> clazz ); + + /** + * For a given root object type returns the base class type to be used in root referenced expressions. This helps in + * some instances where the root objects themselves are compiled javassist instances that need more generic class + * equivalents to cast to. + * + * @param rootNode The root expression node. + * @param context The current execution context. + * @return The root expression class type to cast to for this node. + */ + Class<?> getRootExpressionClass( Node rootNode, OgnlContext context ); + + /** + * Used primarily by AST types like {@link org.apache.commons.ognl.ASTChain} where <code>foo.bar.id</code> type + * references may need to be cast multiple times in order to properly resolve the members in a compiled statement. + * <p> + * This method should be using the various {@link org.apache.commons.ognl.OgnlContext#getCurrentType()} / + * {@link org.apache.commons.ognl.OgnlContext#getCurrentAccessor()} methods to inspect the type stack and properly + * cast to the right classes - but only when necessary. + * </p> + * + * @param context The current execution context. + * @param expression The node being checked for casting. + * @param body The java source string generated by the given node. + * @return The body string parameter plus any additional casting syntax needed to make the expression resolvable. + */ + String castExpression( OgnlContext context, Node expression, String body ); + + /** + * Method is used for expressions where multiple inner parameter method calls in generated java source strings cause + * javassit failures. It is hacky and cumbersome to have to generate expressions this way but it's the only current + * known way to make javassist happy. + * <p> + * Takes an expression block generated by a node and creates a new method on the base object being compiled so that + * sufficiently complicated sub expression blocks can be broken out in to distinct methods to be referenced by the + * core accessor / setter methods in the base compiled root object. + * </p> + * + * @param context The current execution context. + * @param expression The java source expression to dump in to a seperate method reference. + * @param type The return type that should be specified for the new method. + * @return The method name that will be used to reference the sub expression in place of the actual sub expression + * itself. + */ + String createLocalReference( OgnlContext context, String expression, Class<?> type ); +} diff --git a/src/main/java/org/apache/commons/ognl/enhance/OrderedReturn.java b/src/main/java/org/apache/commons/ognl/enhance/OrderedReturn.java index 70ee790..56b91ad 100644 --- a/src/main/java/org/apache/commons/ognl/enhance/OrderedReturn.java +++ b/src/main/java/org/apache/commons/ognl/enhance/OrderedReturn.java @@ -1,44 +1,43 @@ -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. - */ - - -/** - * Marks an ognl expression {@link org.apache.commons.ognl.Node} as needing to have the return portion of a getter - * method happen in a specific - * part of the generated expression vs just having the whole expression returned in one chunk. - */ -public interface OrderedReturn -{ - - /** - * Get the core expression to execute first before any return foo logic is started. - * - * @return The core standalone expression that shouldn't be pre-pended with a return keyword. - */ - String getCoreExpression(); - - /** - * Gets the last expression to be pre-pended with a return <expression> block. - * - * @return The expression representing the return portion of a statement; - */ - String getLastExpression(); -} +/* + * 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; + +/** + * Marks an ognl expression {@link org.apache.commons.ognl.Node} as needing to have the return portion of a getter + * method happen in a specific + * part of the generated expression vs just having the whole expression returned in one chunk. + */ +public interface OrderedReturn +{ + + /** + * Get the core expression to execute first before any return foo logic is started. + * + * @return The core standalone expression that shouldn't be pre-pended with a return keyword. + */ + String getCoreExpression(); + + /** + * Gets the last expression to be pre-pended with a return <expression> block. + * + * @return The expression representing the return portion of a statement; + */ + String getLastExpression(); +} diff --git a/src/main/java/org/apache/commons/ognl/enhance/UnsupportedCompilationException.java b/src/main/java/org/apache/commons/ognl/enhance/UnsupportedCompilationException.java index 24f9a72..9639799 100644 --- a/src/main/java/org/apache/commons/ognl/enhance/UnsupportedCompilationException.java +++ b/src/main/java/org/apache/commons/ognl/enhance/UnsupportedCompilationException.java @@ -1,48 +1,48 @@ -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. - */ - -/** - * Thrown during bytecode enhancement conversions of ognl expressions to indicate that a certain expression isn't - * currently supported as a pure java bytecode enhanced version. - * <p> - * If this exception is thrown it is expected that ognl will fall back to default ognl evaluation of the expression. - * </p> - */ -public class UnsupportedCompilationException - extends RuntimeException -{ - - /** - * - */ - private static final long serialVersionUID = 4961625077128174947L; - - public UnsupportedCompilationException( String message ) - { - super( message ); - } - - public UnsupportedCompilationException( String message, Throwable cause ) - { - super( message, cause ); - } - -} +/* + * 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; + +/** + * Thrown during bytecode enhancement conversions of ognl expressions to indicate that a certain expression isn't + * currently supported as a pure java bytecode enhanced version. + * <p> + * If this exception is thrown it is expected that ognl will fall back to default ognl evaluation of the expression. + * </p> + */ +public class UnsupportedCompilationException + extends RuntimeException +{ + + /** + * + */ + private static final long serialVersionUID = 4961625077128174947L; + + public UnsupportedCompilationException( String message ) + { + super( message ); + } + + public UnsupportedCompilationException( String message, Throwable cause ) + { + super( message, cause ); + } + +} diff --git a/src/main/java/org/apache/commons/ognl/enhance/package-info.java b/src/main/java/org/apache/commons/ognl/enhance/package-info.java index e96f047..b3b6099 100644 --- a/src/main/java/org/apache/commons/ognl/enhance/package-info.java +++ b/src/main/java/org/apache/commons/ognl/enhance/package-info.java @@ -1,24 +1,24 @@ -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. - */ - -/* - * Enhanced basic Java components. - */ +/* + * 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; + +/* + * Enhanced basic Java components. + */