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) {
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 }