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