View Javadoc

1   package org.openrdf.elmo.impl;
2   
3   import static java.lang.reflect.Modifier.isAbstract;
4   import static java.lang.reflect.Modifier.isFinal;
5   import static java.lang.reflect.Modifier.isProtected;
6   
7   import java.lang.reflect.Constructor;
8   import java.lang.reflect.Method;
9   import java.util.ArrayList;
10  import java.util.Arrays;
11  import java.util.Collection;
12  import java.util.HashMap;
13  import java.util.List;
14  import java.util.Map;
15  
16  import org.openrdf.elmo.Entity;
17  import org.openrdf.elmo.EntitySupport;
18  import org.openrdf.elmo.ImplementationResolver;
19  import org.openrdf.elmo.dynacode.ClassFactory;
20  import org.openrdf.elmo.dynacode.ClassTemplate;
21  import org.openrdf.elmo.dynacode.CodeBuilder;
22  import org.openrdf.elmo.exceptions.ElmoCompositionException;
23  
24  public class AbstractBehaviourClassFactory implements ImplementationResolver {
25  	private static final String GET_ENTITY_METHOD = "getSupportedElmoEntity";
26  	public static final String CLASS_PREFIX = "elmobeans.behaviours.";
27  	private static final String BEAN_FIELD_NAME = "_$elmoBean";
28  	private ClassFactory cp;
29  
30  	public void setClassDefiner(ClassFactory definer) {
31  		this.cp = definer;
32  	}
33  
34  	public Collection<Class<?>> findImplementations(Collection<Class<?>> classes) {
35  		try {
36  			List<Class<?>> result = new ArrayList<Class<?>>();
37  			for (Class<?> c : classes) {
38  				result.add(findClass(c));
39  			}
40  			return result;
41  		} catch (ElmoCompositionException e) {
42  			throw e;
43  		} catch (Exception e) {
44  			throw new ElmoCompositionException(e);
45  		}
46  	}
47  
48  	private Class<?> findClass(Class<?> c) throws Exception {
49  		String name = getClassName(c);
50  		try {
51  			return Class.forName(name, true, cp);
52  		} catch (ClassNotFoundException e1) {
53  			synchronized (cp) {
54  				try {
55  					return Class.forName(name, true, cp);
56  				} catch (ClassNotFoundException e2) {
57  					return createClass(name, c);
58  				}
59  			}
60  		}
61  	
62  	}
63  
64  	private Class<?> createClass(String name, Class<?> c) throws Exception {
65  		ClassTemplate cc = cp.createClassTemplate(name, c);
66  		cc.addInterface(EntitySupport.class);
67  		cc.createField(Entity.class, BEAN_FIELD_NAME);
68  		addConstructor(c, cc);
69  		addEntitySupportMethod(cc);
70  		for (Method m : getMethods(c)) {
71  			if (isFinal(m.getModifiers()))
72  				continue;
73  			if (!isAbstract(m.getModifiers()))
74  				continue;
75  			Class<?> r = m.getReturnType();
76  			Class<?>[] types = m.getParameterTypes();
77  			CodeBuilder code = cc.createTransientMethod(r, m.getName(), types);
78  			if (!Void.TYPE.equals(r)) {
79  				code.code("return ($r) ");
80  			}
81  			if (m.getDeclaringClass().isInterface()) {
82  				code.code("(").castObject(BEAN_FIELD_NAME, m.getDeclaringClass());
83  				code.code(").").code(m.getName()).code("($$);").end();
84  			} else {
85  				code.code(BEAN_FIELD_NAME).code(".getClass().getMethod(");
86  				code.insert(m.getName()).code(", ").insert(types).code(")").code(".invoke(");
87  				code.code(BEAN_FIELD_NAME).code(", $args);").end();
88  			}
89  		}
90  		return cp.createClass(cc);
91  	}
92  
93  	private void addEntitySupportMethod(ClassTemplate cc) {
94  		CodeBuilder method = cc.createMethod(Entity.class, GET_ENTITY_METHOD);
95  		method.code("return ").code(BEAN_FIELD_NAME).code(";").end();
96  	}
97  
98  	private Collection<Method> getMethods(Class<?> c) {
99  		List<Method> methods = new ArrayList<Method>();
100 		methods.addAll(Arrays.asList(c.getMethods()));
101 		HashMap<Object, Method> map = new HashMap<Object, Method>();
102 		Map<Object, Method> pms = getProtectedMethods(c, map);
103 		methods.addAll(pms.values());
104 		return methods;
105 	}
106 
107 	private Map<Object, Method> getProtectedMethods(Class<?> c,
108 			Map<Object, Method> methods) {
109 		if (c == null)
110 			return methods;
111 		for (Method m : c.getDeclaredMethods()) {
112 			if (isProtected(m.getModifiers())) {
113 				Object types = Arrays.asList(m.getParameterTypes());
114 				Object key = Arrays.asList(m.getName(), types);
115 				if (!methods.containsKey(key)) {
116 					methods.put(key, m);
117 				}
118 			}
119 		}
120 		return getProtectedMethods(c.getSuperclass(), methods);
121 	}
122 
123 	private void addConstructor(Class<?> c, ClassTemplate cc) throws Exception {
124 		String type = getConstructorParameterType(c);
125 		StringBuilder body = new StringBuilder();
126 		if (type != null) {
127 			body.append("$proceed((");
128 			body.append(type).append(")");
129 			body.append("$1)");
130 		}
131 		body.append(BEAN_FIELD_NAME).append(" = $1;");
132 		cc.addConstructor(new Class<?>[] { Entity.class }, body.toString());
133 	}
134 
135 	private String getClassName(Class<?> klass) {
136 		return CLASS_PREFIX + klass.getName() + "Behaviour";
137 	}
138 
139 	private String getConstructorParameterType(Class<?> javaClass) throws Exception {
140 		for (Constructor<?> c : javaClass.getConstructors()) {
141 			Class<?>[] param = c.getParameterTypes();
142 			if (param.length == 1 && param[0].isInterface()) {
143 				return param[0].getName();
144 			}
145 		}
146 		return null;
147 	}
148 
149 }