View Javadoc

1   /* #####################################################################
2    * #                        REFLEXIVE FRAMEWORK                        #
3    * #                        ___________________                        #
4    * #                                                                   #
5    * # Reflexive Framework, Copyright (C) 2007 Andries Inze              #
6    * #                                                                   #
7    * # This library  is free software; you can redistribute it and/or    #
8    * # modify it under the terms of the GNU Lesser General Public        #
9    * # License as published by the Free Software Foundation; either      #
10   * # version 2.1 of the License, or (at your option) any later version.#
11   * #                                                                   #
12   * # This library  is distributed in the hope that it will be useful,  #
13   * # but WITHOUT ANY WARRANTY; without even the implied warranty of    #
14   * # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the     #
15   * # GNU Lesser General Public License for more details.               #
16   * #                                                                   #
17   * # You should have received a copy of the GNU Lesser General Public  #
18   * # License along with this program; if not, write to the Free        #
19   * # Software Foundation, Inc., 51 Franklin Street, Fifth Floor,       #
20   * # Boston, MA  02110-1301  USA                                       #
21   * #                                                                   #
22   * #####################################################################
23   */
24  package org.reflexiveframework.proxy;
25  
26  import java.lang.reflect.Method;
27  import java.util.HashMap;
28  import java.util.LinkedList;
29  import java.util.List;
30  
31  
32  
33  import net.sf.cglib.proxy.Enhancer;
34  import net.sf.cglib.proxy.MethodInterceptor;
35  import net.sf.cglib.proxy.MethodProxy;
36  
37  /***
38   * Reflexive provides a clean way to handle string literals, 
39   * by dynamically resolving properties.
40   * 
41   * Create a control object by invoking <code>create</code>.
42   * Only use with control objects. Usage with normal objects is unsupported.
43   * 
44   * @author Andries Inze
45   * 
46   */
47  public class Reflexive implements MethodInterceptor {
48  
49  	/***
50  	 * The instance of the singleton.
51  	 * Making this an on-access loading, since double checked locking is broken.
52  	 */
53  	private static Reflexive instance = new Reflexive();
54  
55  	/*** Handles the mapping between method and the proxy object. */
56  	private HashMap methodToObject;
57  
58  	/***
59  	 * The method name that was invoke.
60  	 */
61  	private List previousMethods;
62  
63  	/***
64  	 * The cglib enchancer
65  	 */
66  	private Enhancer e;
67  
68  	private Reflexive() {
69  		previousMethods = new LinkedList();
70  		e = new Enhancer();
71  		methodToObject = new HashMap();
72  	}
73  
74  	private Object newInstance(Class clazz) {
75  		try {
76  			e.setSuperclass(clazz);
77  			e.setCallback(this);
78  			Object bean = Enhancer.create(clazz, getInstance());
79  			return bean;
80  		} catch (Throwable e) {
81  			throw new Error(e.getMessage());
82  		}
83  	}
84  
85  	/***
86  	 * Creates a new control object.
87  	 * 
88  	 * @param clazz
89  	 * @return
90  	 */
91  	public static Object create(final Class clazz) {
92  		return getInstance().newInstance(clazz);
93  	}
94  
95  	/***
96  	 * @return the instance of the singleton
97  	 */
98  	private final static Reflexive getInstance() {
99  		return instance;
100 	}
101 
102 	/***
103 	 * Resolves the property from the method that was invoked. Usage:
104 	 * <code>Reflexive.resolvePropertyName(myObject.getMyProperty())</code>
105 	 * This would return <code>myProperty</code>
106 	 * 
107 	 * Only get- and is-methods are supported.
108 	 * 
109 	 * @param object
110 	 *            The returned object after invoking the created reflexive
111 	 *            object
112 	 * @return the resolved propertyName
113 	 */
114 	public static String resolvePropertyName(final Object object) {
115 		List previousMethods = getInstance().previousMethods;
116 
117 		String propertyName = 
118 			ReflexiveUtils.convertMethodNameToPropertyName(
119 					((Method)previousMethods.get(previousMethods.size()-1))
120 					.getName());
121 
122 		previousMethods.clear();
123 
124 		return ReflexiveUtils.makeNestedPath(propertyName);
125 	}
126 	
127 	/***
128 	 * @param number the number to process
129 	 * @see resolvePropertyName(Object)
130 	 * @return the resolved propertyName
131 	 */
132 	public static String resolvePropertyName(final long number) {
133 		return resolvePropertyName(null);
134 	}
135 	
136 	/***
137 	 * @param number the number to process
138 	 * @see resolvePropertyName(Object)
139 	 * @return the resolved propertyName
140 	 */
141 	public static String resolvePropertyName(final double number) {
142 		return resolvePropertyName(null);
143 	}
144 
145 	/***
146 	 * @param number the number to process
147 	 * @see resolvePropertyName(Object)
148 	 * @return the resolved propertyName
149 	 */
150 	public static String resolvePropertyName(final boolean number) {
151 		return resolvePropertyName(null);
152 	}
153 	
154 	/***
155 	 * Resolves the property from the method that was invoked. Usage:
156 	 * <code>Reflexive.resolveNestedPropertyName(myObject.getMyProperty().myNestedProperty())</code>
157 	 * This would return <code>myProperty.myNestedProperty</code>
158 	 * 
159 	 * Only get- and is-methods are supported.
160 	 * 
161 	 * @param object
162 	 *            The returned object after invoking the created reflexive
163 	 *            object
164 	 * @return the resolved propertyName
165 	 */
166 	public static String resolveNestedPropertyName(final Object object) {
167 
168 		List previousMethods = getInstance().previousMethods;
169 
170 		String[] propertyNames = new String[previousMethods.size()];
171 
172 		for (int i = 0; i < previousMethods.size(); i++) {
173 			propertyNames[i] = ReflexiveUtils
174 					.convertMethodNameToPropertyName(
175 							((Method) previousMethods.get(i)).getName());
176 		}
177 
178 		previousMethods.clear();
179 
180 		return ReflexiveUtils.makeNestedPath(propertyNames);
181 
182 	}
183 	
184 	/***
185 	 * @see resolveNestedPropertyName(Object)
186 	 * @param value the returned value after the invocation
187 	 * @return the nested propertyPath
188 	 */
189 	public static String resolveNestedPropertyName(final long value) {
190 		return resolveNestedPropertyName(null);
191 	}
192 	
193 	/***
194 	 * @see resolveNestedPropertyName(Object)
195 	 * @param value the returned value after the invocation
196 	 * @return the nested propertyPath
197 	 */
198 	public static String resolveNestedPropertyName(final double value) {
199 		return resolveNestedPropertyName(null);
200 	}
201 	
202 	/***
203 	 * @see resolveNestedPropertyName(Object)
204 	 * @param value the returned value after the invocation
205 	 * @return the nested propertyPath
206 	 */
207 	public static String resolveNestedPropertyName(final boolean value) {
208 		return resolveNestedPropertyName(null);
209 	}
210 
211 	/***
212 	 * @see net.sf.cglib.proxy.MethodInterceptor #intercept(java.lang.Object,
213 	 *      java.lang.reflect.Method, java.lang.Object[],
214 	 *      net.sf.cglib.proxy.MethodProxy)
215 	 */
216 	public Object intercept(final Object object, final Method method,
217 			final Object[] args, final MethodProxy methodProxy)
218 			throws Throwable {
219 
220 		if (method.getName().equals("hashCode")) {
221 			return Integer.valueOf(method.getReturnType().hashCode());
222 		} else if(method.getName().equals("equals")) {
223 			return Boolean.FALSE;
224 		}
225 
226 		if (!method.getName().startsWith("get")
227 				&& !method.getName().startsWith("is")) {
228 			throw new RuntimeException("Only getters (or issers) are supported");
229 		}
230 
231 		if (method.getReturnType().equals(Void.TYPE)) {
232 			throw new RuntimeException("Can't use proxy in void return types");
233 		}
234 
235 		previousMethods.add(method);
236 
237 		if (method.getReturnType().equals(int.class)
238 				|| method.getReturnType().equals(boolean.class)
239 				|| method.getReturnType().equals(short.class)
240 				|| method.getReturnType().equals(double.class)
241 				|| method.getReturnType().equals(char.class)
242 				|| method.getReturnType().equals(long.class)
243 				|| method.getReturnType().equals(float.class)
244 				|| method.getReturnType().equals(byte.class)) {
245 
246 				return null;
247 		} else {
248 			//Returning the cached proxy
249 			if (!methodToObject.containsKey(method)) {
250 				methodToObject.put(method, buildMethodReturnProxy(method));
251 			}
252 			return methodToObject.get(method);
253 		}
254 	}
255 
256 	private Object buildMethodReturnProxy(final Method method) {
257 		Object proxy = null;
258 
259 		if (method.getReturnType().equals(String.class)) {
260 			proxy = ReflexiveUtils.createRandomString();
261 		} else if (method.getReturnType().equals(Boolean.class)) {
262 			proxy = new Boolean(true);
263 		} else if (method.getReturnType().equals(Integer.class)) {
264 			proxy = new Integer(1000);
265 		} else if (method.getReturnType().equals(Double.class)) {
266 			proxy = new Double(1000.0);
267 		} else if (method.getReturnType().equals(Long.class)) {
268 			proxy = new Long(1000L);
269 		} else if (method.getReturnType().equals(Float.class)) {
270 			proxy = new Float(1000L);
271 		} else if (method.getReturnType().equals(Character.class)) {
272 			proxy = new Character('x');
273 		} else if (method.getReturnType().equals(Short.class)) {
274 			proxy = new Short((short) 1);
275 		} else if (method.getReturnType().equals(Byte.class)) {
276 			proxy = new Byte((byte) 1);
277 		} else {
278 			// TODO: return null, with previousMethod, if proxy creation
279 			// fails.
280 			e.setSuperclass(method.getReturnType());
281 			e.setCallback(this);
282 			Object bean = Enhancer
283 					.create(method.getReturnType(), getInstance());
284 			return bean;
285 		}
286 		return proxy;
287 	}
288 }