View Javadoc

1   package net.sf.twip.parameterhandler;
2   
3   import java.lang.reflect.*;
4   import java.util.*;
5   
6   import net.sf.twip.AutoTwip;
7   import net.sf.twip.internal.*;
8   import net.sf.twip.util.Parameter;
9   
10  public class AutoTwipParameterHandler extends ParameterHandler {
11  
12  	private static abstract class AbstractAutoTwipMethodHandler implements AutoTwipHandler {
13  		private final Method method;
14  
15  		public AbstractAutoTwipMethodHandler(Method method) {
16  			this.method = method;
17  		}
18  
19  		public abstract void invoke(FrameworkMethodWithArgs method, List<Object> result)
20  				throws Throwable;
21  
22  		private TwipConfigurationErrorCallingMethod newMethodCallingError(
23  				final FrameworkMethodWithArgs parameterizedMethod, final Throwable e) {
24  			return new TwipConfigurationErrorCallingMethod("can't initialize AutoTwip values from "
25  					+ parameterizedMethod, e);
26  		}
27  
28  		public Object[] run() {
29  			final List<Object> result = new ArrayList<Object>();
30  			for (final FrameworkMethodWithArgs parameterizedMethod : FrameworkMethodWithArgs
31  					.of(method)) {
32  				try {
33  					invoke(parameterizedMethod, result);
34  				} catch (final ThreadDeath e) {
35  					throw e;
36  				} catch (final VirtualMachineError e) {
37  					throw e;
38  				} catch (final LinkageError e) {
39  					throw e;
40  				} catch (final Throwable e) { // NOSONAR
41  					throw newMethodCallingError(parameterizedMethod, e);
42  				}
43  			}
44  			return result.toArray();
45  		}
46  	}
47  
48  	private static class AutoTwipFieldArrayHandler implements AutoTwipHandler {
49  
50  		private final Field field;
51  
52  		public AutoTwipFieldArrayHandler(Field field) {
53  			this.field = field;
54  		}
55  
56  		public Object[] run() {
57  			try {
58  				return (Object[]) field.get(null);
59  			} catch (final Exception e) {
60  				throw new TwipConfigurationErrorGettingField("while calling AutoTwip field "
61  						+ field, e);
62  			}
63  		}
64  	}
65  
66  	private static class AutoTwipFieldCollectionHandler implements AutoTwipHandler {
67  
68  		private final Field field;
69  
70  		public AutoTwipFieldCollectionHandler(Field field) {
71  			this.field = field;
72  		}
73  
74  		public Object[] run() {
75  			try {
76  				return ((Collection<?>) field.get(null)).toArray();
77  			} catch (final Exception e) {
78  				throw new TwipConfigurationErrorGettingField("while calling AutoTwip field "
79  						+ field, e);
80  			}
81  		}
82  	}
83  
84  	private interface AutoTwipHandler {
85  		public Object[] run();
86  	}
87  
88  	private static class AutoTwipMethodArrayHandler extends AbstractAutoTwipMethodHandler {
89  
90  		public AutoTwipMethodArrayHandler(Method method) {
91  			super(method);
92  		}
93  
94  		@Override
95  		public void invoke(FrameworkMethodWithArgs method, List<Object> result) throws Throwable {
96  			for (final Object object : ((Object[]) method.invokeExplosively(null))) {
97  				result.add(object);
98  			}
99  		}
100 	}
101 
102 	private static class AutoTwipMethodCollectionHandler extends AbstractAutoTwipMethodHandler {
103 
104 		public AutoTwipMethodCollectionHandler(Method method) {
105 			super(method);
106 		}
107 
108 		@Override
109 		public void invoke(FrameworkMethodWithArgs method, List<Object> result) throws Throwable {
110 			for (final Object object : ((Collection<?>) method.invokeExplosively(null))) {
111 				result.add(object);
112 			}
113 		}
114 	}
115 
116 	private static class AutoTwipMethodResultHandler extends AbstractAutoTwipMethodHandler {
117 
118 		public AutoTwipMethodResultHandler(Method method) {
119 			super(method);
120 		}
121 
122 		@Override
123 		public void invoke(FrameworkMethodWithArgs method, List<Object> result) throws Throwable {
124 			final Object object = method.invokeExplosively(null);
125 			result.add(object);
126 		}
127 	}
128 
129 	private final AutoTwipHandler autoTwipHandler;
130 
131 	public AutoTwipParameterHandler(Parameter parameter) {
132 		super(parameter);
133 		this.autoTwipHandler = findAutoTwipHandlers().get(parameter.getType());
134 	}
135 
136 	private void fillAutoTwipFields(Map<Class<?>, AutoTwipHandler> handlers, Class<?> declaringClass) {
137 		for (final Field field : declaringClass.getDeclaredFields()) {
138 			if (field.isAnnotationPresent(AutoTwip.class)) {
139 				if (!Modifier.isStatic(field.getModifiers()))
140 					throw new TwipConfigurationErrorNonStatic("the field " + field
141 							+ " is annotated as @AutoTwip, but it is not static!");
142 				Class<?> type = field.getType();
143 				AutoTwipHandler handler;
144 				if (type.isArray()) {
145 					type = type.getComponentType();
146 					handler = new AutoTwipFieldArrayHandler(field);
147 				} else if (Collection.class.isAssignableFrom(type)) {
148 					final ParameterizedType parameterizedType = (ParameterizedType) field
149 							.getGenericType();
150 					type = (Class<?>) parameterizedType.getActualTypeArguments()[0];
151 					handler = new AutoTwipFieldCollectionHandler(field);
152 				} else {
153 					throw new TwipConfigurationErrorNotArrayOrCollection("the field " + field
154 							+ " is annotated as @AutoTwip, but it's not an array or collection!");
155 
156 				}
157 				final AutoTwipHandler old = handlers.put(type, handler);
158 				if (old != null) {
159 					throw new TwipConfigurationErrorDuplicateAutoTwip(
160 							"the field "
161 									+ field
162 									+ " is annotated as @AutoTwip, but it's of the same type as the AutoTwip "
163 									+ old);
164 				}
165 			}
166 		}
167 	}
168 
169 	private void fillAutoTwipMethods(Map<Class<?>, AutoTwipHandler> handlers,
170 			Class<?> declaringClass) {
171 		for (final Method method : declaringClass.getDeclaredMethods()) {
172 			if (method.isAnnotationPresent(AutoTwip.class)) {
173 				if (!Modifier.isStatic(method.getModifiers()))
174 					throw new TwipConfigurationErrorNonStatic("the method " + method
175 							+ " is annotated as @AutoTwip, but it is not static!");
176 				Class<?> type = method.getReturnType();
177 				if (Void.TYPE.equals(type))
178 					throw new TwipConfigurationErrorVoidAutoTwipMethod("the method " + method
179 							+ " is annotated as @AutoTwip, but it returns void!");
180 				AutoTwipHandler handler;
181 				if (type.isArray()) {
182 					type = type.getComponentType();
183 					handler = new AutoTwipMethodArrayHandler(method);
184 				} else if (Collection.class.isAssignableFrom(type)) {
185 					final ParameterizedType parameterizedType = (ParameterizedType) method
186 							.getGenericReturnType();
187 					type = (Class<?>) parameterizedType.getActualTypeArguments()[0];
188 					handler = new AutoTwipMethodCollectionHandler(method);
189 				} else {
190 					handler = new AutoTwipMethodResultHandler(method);
191 				}
192 				final AutoTwipHandler old = handlers.put(type, handler);
193 				if (old != null) {
194 					throw new TwipConfigurationErrorDuplicateAutoTwip(
195 							"the method "
196 									+ method
197 									+ " is annotated as @AutoTwip, but it returns the same type as the AutoTwip "
198 									+ old);
199 				}
200 			}
201 		}
202 	}
203 
204 	private Map<Class<?>, AutoTwipHandler> findAutoTwipHandlers() {
205 		Class<?> declaringClass = parameter.getMethod().getDeclaringClass();
206 		final Map<Class<?>, AutoTwipHandler> handlers = new HashMap<Class<?>, AutoTwipHandler>();
207 		fillAutoTwipMethods(handlers, declaringClass);
208 		fillAutoTwipFields(handlers, declaringClass);
209 		return handlers;
210 	}
211 
212 	@Override
213 	protected Object[] getDefaultParameterValues() {
214 		if (autoTwipHandler == null)
215 			throw new TwipConfigurationErrorUnknownParameterType(
216 					"TwiP can't produce parameters of type [" + parameter.getType().getName()
217 							+ "]. Please add a @Value annotation to " + parameter
218 							+ " or declare a @AutoTwip field or method.");
219 		final Object[] result = autoTwipHandler.run();
220 		if (result == null)
221 			throw new TwipConfigurationErrorNullPointer("The AutoTwip " + autoTwipHandler
222 					+ " is/returns null");
223 		return result;
224 	}
225 }