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