1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.statemachine.transition;
21
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
24 import java.util.Arrays;
25
26 import org.apache.commons.lang.builder.EqualsBuilder;
27 import org.apache.commons.lang.builder.HashCodeBuilder;
28 import org.apache.commons.lang.builder.ToStringBuilder;
29 import org.apache.mina.statemachine.State;
30 import org.apache.mina.statemachine.StateMachine;
31 import org.apache.mina.statemachine.StateMachineFactory;
32 import org.apache.mina.statemachine.annotation.Transition;
33 import org.apache.mina.statemachine.context.StateContext;
34 import org.apache.mina.statemachine.event.Event;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 public class MethodTransition extends AbstractTransition {
62 private static final Logger log = LoggerFactory.getLogger( MethodTransition.class );
63 private static final Object[] EMPTY_ARGUMENTS = new Object[0];
64
65 private final Method method;
66 private final Object target;
67
68
69
70
71
72
73
74
75
76
77 public MethodTransition(Object eventId, State nextState, Method method, Object target) {
78 super(eventId, nextState);
79 this.method = method;
80 this.target = target;
81 }
82
83
84
85
86
87
88
89
90
91 public MethodTransition(Object eventId, Method method, Object target) {
92 this(eventId, null, method, target);
93 }
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 public MethodTransition(Object eventId, State nextState, Object target) {
110 this(eventId, nextState, eventId.toString(), target);
111 }
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126 public MethodTransition(Object eventId, Object target) {
127 this(eventId, eventId.toString(), target);
128 }
129
130
131
132
133
134
135
136
137
138
139
140
141 public MethodTransition(Object eventId, String methodName, Object target) {
142 this(eventId, null, methodName, target);
143 }
144
145
146
147
148
149
150
151
152
153
154
155
156
157 public MethodTransition(Object eventId, State nextState, String methodName, Object target) {
158 super(eventId, nextState);
159
160 this.target = target;
161
162 Method[] candidates = target.getClass().getMethods();
163 Method result = null;
164 for (int i = 0; i < candidates.length; i++) {
165 if (candidates[i].getName().equals(methodName)) {
166 if (result != null) {
167 throw new AmbiguousMethodException(methodName);
168 }
169 result = candidates[i];
170 }
171 }
172
173 if (result == null) {
174 throw new NoSuchMethodException(methodName);
175 }
176
177 this.method = result;
178 }
179
180
181
182
183
184
185 public Method getMethod() {
186 return method;
187 }
188
189
190
191
192
193
194 public Object getTarget() {
195 return target;
196 }
197
198 public boolean doExecute(Event event) {
199 Class<?>[] types = method.getParameterTypes();
200
201 if (types.length == 0) {
202 invokeMethod(EMPTY_ARGUMENTS);
203 return true;
204 }
205
206 if (types.length > 2 + event.getArguments().length) {
207 return false;
208 }
209
210 Object[] args = new Object[types.length];
211
212 int i = 0;
213 if (match(types[i], event, Event.class)) {
214 args[i++] = event;
215 }
216 if (i < args.length && match(types[i], event.getContext(), StateContext.class)) {
217 args[i++] = event.getContext();
218 }
219 Object[] eventArgs = event.getArguments();
220 for (int j = 0; i < args.length && j < eventArgs.length; j++) {
221 if (match(types[i], eventArgs[j], Object.class)) {
222 args[i++] = eventArgs[j];
223 }
224 }
225
226 if (args.length > i) {
227 return false;
228 }
229
230 invokeMethod(args);
231
232 return true;
233 }
234
235 @SuppressWarnings("unchecked")
236 private boolean match(Class<?> paramType, Object arg, Class argType) {
237 if (paramType.isPrimitive()) {
238 if (paramType.equals(Boolean.TYPE)) {
239 return arg instanceof Boolean;
240 }
241 if (paramType.equals(Integer.TYPE)) {
242 return arg instanceof Integer;
243 }
244 if (paramType.equals(Long.TYPE)) {
245 return arg instanceof Long;
246 }
247 if (paramType.equals(Short.TYPE)) {
248 return arg instanceof Short;
249 }
250 if (paramType.equals(Byte.TYPE)) {
251 return arg instanceof Byte;
252 }
253 if (paramType.equals(Double.TYPE)) {
254 return arg instanceof Double;
255 }
256 if (paramType.equals(Float.TYPE)) {
257 return arg instanceof Float;
258 }
259 if (paramType.equals(Character.TYPE)) {
260 return arg instanceof Character;
261 }
262 }
263 return argType.isAssignableFrom(paramType)
264 && paramType.isAssignableFrom(arg.getClass());
265 }
266
267 private void invokeMethod(Object[] arguments) {
268 try {
269 if (log.isDebugEnabled()) {
270 log.debug("Executing method " + method
271 + " with arguments " + Arrays.asList(arguments));
272 }
273 method.invoke(target, arguments);
274 } catch (InvocationTargetException ite) {
275 if (ite.getCause() instanceof RuntimeException) {
276 throw (RuntimeException) ite.getCause();
277 }
278 throw new MethodInvocationException(method, ite);
279 } catch (IllegalAccessException iae) {
280 throw new MethodInvocationException(method, iae);
281 }
282 }
283
284 public boolean equals(Object o) {
285 if (!(o instanceof MethodTransition)) {
286 return false;
287 }
288 if (o == this) {
289 return true;
290 }
291 MethodTransition that = (MethodTransition) o;
292 return new EqualsBuilder()
293 .appendSuper(super.equals(that))
294 .append(method, that.method)
295 .append(target, that.target)
296 .isEquals();
297 }
298
299 public int hashCode() {
300 return new HashCodeBuilder(13, 33).appendSuper(super.hashCode()).append(method).append(target).toHashCode();
301 }
302
303 public String toString() {
304 return new ToStringBuilder(this)
305 .appendSuper(super.toString())
306 .append("method", method)
307 .append("target", target)
308 .toString();
309 }
310 }