001: //--------------------------------------------------------------------------
002: // Copyright (c) 1998-2004, Drew Davidson and Luke Blanshard
003: // All rights reserved.
004: //
005: // Redistribution and use in source and binary forms, with or without
006: // modification, are permitted provided that the following conditions are
007: // met:
008: //
009: // Redistributions of source code must retain the above copyright notice,
010: // this list of conditions and the following disclaimer.
011: // Redistributions in binary form must reproduce the above copyright
012: // notice, this list of conditions and the following disclaimer in the
013: // documentation and/or other materials provided with the distribution.
014: // Neither the name of the Drew Davidson nor the names of its contributors
015: // may be used to endorse or promote products derived from this software
016: // without specific prior written permission.
017: //
018: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
019: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
020: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
021: // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
022: // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
023: // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
024: // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
025: // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
026: // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
027: // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
028: // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
029: // DAMAGE.
030: //--------------------------------------------------------------------------
031: package org.ognl.test;
032:
033: import java.lang.reflect.Method;
034: import java.lang.reflect.Modifier;
035: import java.util.HashMap;
036: import java.util.IdentityHashMap;
037: import java.util.Map;
038: import javassist.ClassPool;
039: import javassist.CtClass;
040: import javassist.CtMethod;
041: import javassist.LoaderClassPath;
042: import ognl.ObjectPropertyAccessor;
043: import ognl.OgnlContext;
044: import ognl.OgnlException;
045: import ognl.OgnlRuntime;
046: import org.ognl.test.util.ContextClassLoader;
047: import org.ognl.test.util.EnhancedClassLoader;
048: import org.ognl.test.util.NameFactory;
049:
050: /**
051: * Implementation of PropertyAccessor that uses Javassist to compile
052: * a property accessor specifically tailored to the property.
053: */
054: public class CompilingPropertyAccessor extends ObjectPropertyAccessor {
055: private static NameFactory NAME_FACTORY = new NameFactory(
056: "ognl.PropertyAccessor", "v");
057: private static Getter NotFoundGetter = new Getter() {
058: public Object get(OgnlContext context, Object target,
059: String propertyName) {
060: return null;
061: }
062: };
063: private static Getter DefaultGetter = new Getter() {
064: public Object get(OgnlContext context, Object target,
065: String propertyName) {
066: try {
067: return OgnlRuntime.getMethodValue(context, target,
068: propertyName, true);
069: } catch (Exception ex) {
070: throw new RuntimeException(ex);
071: }
072: }
073: };
074: private static Map pools = new HashMap();
075: private static Map loaders = new HashMap();
076:
077: private static java.util.IdentityHashMap PRIMITIVE_WRAPPER_CLASSES = new IdentityHashMap();
078: private java.util.IdentityHashMap seenGetMethods = new java.util.IdentityHashMap();
079:
080: static {
081: PRIMITIVE_WRAPPER_CLASSES.put(Boolean.TYPE, Boolean.class);
082: PRIMITIVE_WRAPPER_CLASSES.put(Boolean.class, Boolean.TYPE);
083: PRIMITIVE_WRAPPER_CLASSES.put(Byte.TYPE, Byte.class);
084: PRIMITIVE_WRAPPER_CLASSES.put(Byte.class, Byte.TYPE);
085: PRIMITIVE_WRAPPER_CLASSES.put(Character.TYPE, Character.class);
086: PRIMITIVE_WRAPPER_CLASSES.put(Character.class, Character.TYPE);
087: PRIMITIVE_WRAPPER_CLASSES.put(Short.TYPE, Short.class);
088: PRIMITIVE_WRAPPER_CLASSES.put(Short.class, Short.TYPE);
089: PRIMITIVE_WRAPPER_CLASSES.put(Integer.TYPE, Integer.class);
090: PRIMITIVE_WRAPPER_CLASSES.put(Integer.class, Integer.TYPE);
091: PRIMITIVE_WRAPPER_CLASSES.put(Long.TYPE, Long.class);
092: PRIMITIVE_WRAPPER_CLASSES.put(Long.class, Long.TYPE);
093: PRIMITIVE_WRAPPER_CLASSES.put(Float.TYPE, Float.class);
094: PRIMITIVE_WRAPPER_CLASSES.put(Float.class, Float.TYPE);
095: PRIMITIVE_WRAPPER_CLASSES.put(Double.TYPE, Double.class);
096: PRIMITIVE_WRAPPER_CLASSES.put(Double.class, Double.TYPE);
097: }
098:
099: public static Class getPrimitiveWrapperClass(Class primitiveClass) {
100: return (Class) PRIMITIVE_WRAPPER_CLASSES.get(primitiveClass);
101: }
102:
103: public interface Getter {
104: public Object get(OgnlContext context, Object target,
105: String propertyName);
106: }
107:
108: public static Getter generateGetter(OgnlContext context, String code)
109: throws OgnlException {
110: String className = NAME_FACTORY.getNewClassName();
111:
112: try {
113: ClassPool pool = (ClassPool) pools.get(context
114: .getClassResolver());
115: EnhancedClassLoader loader = (EnhancedClassLoader) loaders
116: .get(context.getClassResolver());
117: CtClass newClass;
118: CtClass ognlContextClass;
119: CtClass objectClass;
120: CtClass stringClass;
121: CtMethod method;
122: byte[] byteCode;
123: Class compiledClass;
124:
125: if ((pool == null) || (loader == null)) {
126: ClassLoader classLoader = new ContextClassLoader(
127: OgnlContext.class.getClassLoader(), context);
128:
129: pool = ClassPool.getDefault();
130: pool.insertClassPath(new LoaderClassPath(classLoader));
131: pools.put(context.getClassResolver(), pool);
132:
133: loader = new EnhancedClassLoader(classLoader);
134: loaders.put(context.getClassResolver(), loader);
135: }
136:
137: newClass = pool.makeClass(className);
138: ognlContextClass = pool.get(OgnlContext.class.getName());
139: objectClass = pool.get(Object.class.getName());
140: stringClass = pool.get(String.class.getName());
141:
142: newClass.addInterface(pool.get(Getter.class.getName()));
143: method = new CtMethod(objectClass, "get", new CtClass[] {
144: ognlContextClass, objectClass, stringClass },
145: newClass);
146: method.setBody("{" + code + "}");
147: newClass.addMethod(method);
148: byteCode = newClass.toBytecode();
149: compiledClass = loader.defineClass(className, byteCode);
150: return (Getter) compiledClass.newInstance();
151: } catch (Throwable ex) {
152: throw new OgnlException("Cannot create class", ex);
153: }
154: }
155:
156: private Getter getGetter(OgnlContext context, Object target,
157: String propertyName) throws OgnlException {
158: Getter result;
159: Class targetClass = target.getClass();
160: Map propertyMap;
161:
162: if ((propertyMap = (Map) seenGetMethods.get(targetClass)) == null) {
163: propertyMap = new HashMap(101);
164: seenGetMethods.put(targetClass, propertyMap);
165: }
166: if ((result = (Getter) propertyMap.get(propertyName)) == null) {
167: try {
168: Method method = OgnlRuntime.getGetMethod(context,
169: targetClass, propertyName);
170:
171: if (method != null) {
172: if (Modifier.isPublic(method.getModifiers())) {
173: if (method.getReturnType().isPrimitive()) {
174: propertyMap
175: .put(
176: propertyName,
177: result = generateGetter(
178: context,
179: "java.lang.Object\t\tresult;\n"
180: + targetClass
181: .getName()
182: + "\t"
183: + "t0 = ("
184: + targetClass
185: .getName()
186: + ")$2;\n"
187: + "\n"
188: + "try {\n"
189: + " result = new "
190: + getPrimitiveWrapperClass(
191: method
192: .getReturnType())
193: .getName()
194: + "(t0."
195: + method
196: .getName()
197: + "());\n"
198: + "} catch (java.lang.Exception ex) {\n"
199: + " throw new java.lang.RuntimeException(ex);\n"
200: + "}\n"
201: + "return result;"));
202: } else {
203: propertyMap
204: .put(
205: propertyName,
206: result = generateGetter(
207: context,
208: "java.lang.Object\t\tresult;\n"
209: + targetClass
210: .getName()
211: + "\t"
212: + "t0 = ("
213: + targetClass
214: .getName()
215: + ")$2;\n"
216: + "\n"
217: + "try {\n"
218: + " result = t0."
219: + method
220: .getName()
221: + "();\n"
222: + "} catch (java.lang.Exception ex) {\n"
223: + " throw new java.lang.RuntimeException(ex);\n"
224: + "}\n"
225: + "return result;"));
226: }
227: } else {
228: propertyMap.put(propertyName,
229: result = DefaultGetter);
230: }
231: } else {
232: propertyMap.put(propertyName,
233: result = NotFoundGetter);
234: }
235: } catch (Exception ex) {
236: throw new OgnlException("getting getter", ex);
237: }
238: }
239: return result;
240: }
241:
242: /**
243: Returns OgnlRuntime.NotFound if the property does not exist.
244: */
245: public Object getPossibleProperty(Map context, Object target,
246: String name) throws OgnlException {
247: Object result;
248: OgnlContext ognlContext = (OgnlContext) context;
249:
250: if (context.get("_compile") != null) {
251: Getter getter = getGetter(ognlContext, target, name);
252:
253: if (getter != NotFoundGetter) {
254: result = getter.get(ognlContext, target, name);
255: } else {
256: try {
257: result = OgnlRuntime.getFieldValue(ognlContext,
258: target, name, true);
259: } catch (Exception ex) {
260: throw new OgnlException(name, ex);
261: }
262: }
263: } else {
264: result = super.getPossibleProperty(context, target, name);
265: }
266: return result;
267: }
268: }
|