Mercurial Hosting > luan
comparison src/luan/LuanJavaFunction.java @ 48:64ecb7a3aad7
rename Lua to Luan
git-svn-id: https://luan-java.googlecode.com/svn/trunk@49 21e917c8-12df-6dd8-5cb6-c86387c605b9
author | fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9> |
---|---|
date | Fri, 28 Dec 2012 03:29:12 +0000 |
parents | src/luan/LuaJavaFunction.java@659c7139e903 |
children | 8ede219cd111 |
comparison
equal
deleted
inserted
replaced
47:659c7139e903 | 48:64ecb7a3aad7 |
---|---|
1 package luan; | |
2 | |
3 import java.lang.reflect.Array; | |
4 import java.lang.reflect.Method; | |
5 import java.lang.reflect.Constructor; | |
6 import java.lang.reflect.InvocationTargetException; | |
7 import java.util.List; | |
8 import java.util.Map; | |
9 import java.util.Set; | |
10 | |
11 | |
12 public final class LuanJavaFunction extends LuanFunction { | |
13 private final JavaMethod method; | |
14 private final Object obj; | |
15 private final RtnConverter rtnConverter; | |
16 private final boolean takesLuaState; | |
17 private final ArgConverter[] argConverters; | |
18 private final Class<?> varArgCls; | |
19 | |
20 public LuanJavaFunction(Method method,Object obj) { | |
21 this( JavaMethod.of(method), obj ); | |
22 } | |
23 | |
24 public LuanJavaFunction(Constructor constr,Object obj) { | |
25 this( JavaMethod.of(constr), obj ); | |
26 } | |
27 | |
28 LuanJavaFunction(JavaMethod method,Object obj) { | |
29 this.method = method; | |
30 this.obj = obj; | |
31 this.rtnConverter = getRtnConverter(method); | |
32 this.takesLuaState = takesLuaState(method); | |
33 this.argConverters = getArgConverters(takesLuaState,method); | |
34 if( method.isVarArgs() ) { | |
35 Class<?>[] paramTypes = method.getParameterTypes(); | |
36 this.varArgCls = paramTypes[paramTypes.length-1].getComponentType(); | |
37 } else { | |
38 this.varArgCls = null; | |
39 } | |
40 } | |
41 | |
42 public Class<?>[] getParameterTypes() { | |
43 return method.getParameterTypes(); | |
44 } | |
45 | |
46 @Override public Object[] call(LuanState lua,Object[] args) throws LuanException { | |
47 args = fixArgs(lua,args); | |
48 Object rtn; | |
49 try { | |
50 rtn = method.invoke(obj,args); | |
51 } catch(IllegalArgumentException e) { | |
52 checkArgs(lua,args); | |
53 throw e; | |
54 } catch(IllegalAccessException e) { | |
55 throw new RuntimeException(e); | |
56 } catch(InvocationTargetException e) { | |
57 Throwable cause = e.getCause(); | |
58 if( cause instanceof Error ) | |
59 throw (Error)cause; | |
60 if( cause instanceof RuntimeException ) | |
61 throw (RuntimeException)cause; | |
62 if( cause instanceof LuanException ) | |
63 throw (LuanException)cause; | |
64 throw new RuntimeException(e); | |
65 } catch(InstantiationException e) { | |
66 throw new RuntimeException(e); | |
67 } | |
68 return rtnConverter.convert(rtn); | |
69 } | |
70 | |
71 private void checkArgs(LuanState lua,Object[] args) throws LuanException { | |
72 Class<?>[] a = getParameterTypes(); | |
73 for( int i=0; i<a.length; i++ ) { | |
74 if( !a[i].isInstance(args[i]) ) { | |
75 String got = args[i].getClass().getName(); | |
76 String expected = a[i].getName(); | |
77 if( !takesLuaState ) | |
78 i++; | |
79 throw new LuanException(lua,LuanElement.JAVA,"bad argument #"+i+" ("+expected+" expected, got "+got+")"); | |
80 } | |
81 } | |
82 } | |
83 | |
84 private Object[] fixArgs(LuanState lua,Object[] args) { | |
85 int n = argConverters.length; | |
86 Object[] rtn; | |
87 int start = 0; | |
88 if( !takesLuaState && varArgCls==null && args.length == n ) { | |
89 rtn = args; | |
90 } else { | |
91 if( takesLuaState ) | |
92 n++; | |
93 rtn = new Object[n]; | |
94 if( takesLuaState ) { | |
95 rtn[start++] = lua; | |
96 } | |
97 n = argConverters.length; | |
98 if( varArgCls != null ) { | |
99 n--; | |
100 if( args.length < argConverters.length ) { | |
101 rtn[rtn.length-1] = Array.newInstance(varArgCls,0); | |
102 } else { | |
103 int len = args.length - n; | |
104 Object varArgs = Array.newInstance(varArgCls,len); | |
105 ArgConverter ac = argConverters[n]; | |
106 for( int i=0; i<len; i++ ) { | |
107 Array.set( varArgs, i, ac.convert(args[n+i]) ); | |
108 } | |
109 rtn[rtn.length-1] = varArgs; | |
110 } | |
111 } | |
112 System.arraycopy(args,0,rtn,start,Math.min(args.length,n)); | |
113 } | |
114 for( int i=0; i<n; i++ ) { | |
115 rtn[start+i] = argConverters[i].convert(rtn[start+i]); | |
116 } | |
117 return rtn; | |
118 } | |
119 | |
120 | |
121 private interface RtnConverter { | |
122 public Object[] convert(Object obj); | |
123 } | |
124 | |
125 private static final RtnConverter RTN_EMPTY = new RtnConverter() { | |
126 public Object[] convert(Object obj) { | |
127 return EMPTY_RTN; | |
128 } | |
129 }; | |
130 | |
131 private static final RtnConverter RTN_ARRAY = new RtnConverter() { | |
132 public Object[] convert(Object obj) { | |
133 if( obj == null ) | |
134 return NULL_RTN; | |
135 return (Object[])obj; | |
136 } | |
137 }; | |
138 | |
139 private static final RtnConverter RTN_ONE = new RtnConverter() { | |
140 public Object[] convert(Object obj) { | |
141 return new Object[]{obj}; | |
142 } | |
143 }; | |
144 | |
145 private static final Object[] NULL_RTN = new Object[1]; | |
146 | |
147 private static final RtnConverter RTN_NUMBER_ARRAY = new RtnConverter() { | |
148 public Object[] convert(Object obj) { | |
149 if( obj == null ) | |
150 return NULL_RTN; | |
151 Object[] rtn = new Object[Array.getLength(obj)]; | |
152 for( int i=0; i<rtn.length; i++ ) { | |
153 rtn[i] = Array.get(obj,i); | |
154 } | |
155 return rtn; | |
156 } | |
157 }; | |
158 | |
159 private static RtnConverter getRtnConverter(JavaMethod m) { | |
160 Class<?> rtnType = m.getReturnType(); | |
161 if( rtnType == Void.TYPE ) | |
162 return RTN_EMPTY; | |
163 if( rtnType.isArray() ) { | |
164 rtnType = rtnType.getComponentType(); | |
165 if( isNumber(rtnType) ) | |
166 return RTN_NUMBER_ARRAY; | |
167 return RTN_ARRAY; | |
168 } | |
169 return RTN_ONE; | |
170 } | |
171 | |
172 private static boolean isNumber(Class<?> rtnType) { | |
173 return rtnType == Byte.TYPE | |
174 || rtnType == Short.TYPE | |
175 || rtnType == Integer.TYPE | |
176 || rtnType == Long.TYPE | |
177 || rtnType == Float.TYPE | |
178 || rtnType == Double.TYPE | |
179 ; | |
180 } | |
181 | |
182 private interface ArgConverter { | |
183 public Object convert(Object obj); | |
184 } | |
185 | |
186 private static final ArgConverter ARG_SAME = new ArgConverter() { | |
187 public Object convert(Object obj) { | |
188 return obj; | |
189 } | |
190 }; | |
191 | |
192 private static final ArgConverter ARG_DOUBLE = new ArgConverter() { | |
193 public Object convert(Object obj) { | |
194 if( obj instanceof Double ) | |
195 return obj; | |
196 if( obj instanceof Number ) { | |
197 Number n = (Number)obj; | |
198 return n.doubleValue(); | |
199 } | |
200 if( obj instanceof String ) { | |
201 String s = (String)obj; | |
202 try { | |
203 return Double.valueOf(s); | |
204 } catch(NumberFormatException e) {} | |
205 } | |
206 return obj; | |
207 } | |
208 }; | |
209 | |
210 private static final ArgConverter ARG_FLOAT = new ArgConverter() { | |
211 public Object convert(Object obj) { | |
212 if( obj instanceof Float ) | |
213 return obj; | |
214 if( obj instanceof Number ) { | |
215 Number n = (Number)obj; | |
216 float r = n.floatValue(); | |
217 if( r==n.doubleValue() ) | |
218 return r; | |
219 } | |
220 if( obj instanceof String ) { | |
221 String s = (String)obj; | |
222 try { | |
223 return Float.valueOf(s); | |
224 } catch(NumberFormatException e) {} | |
225 } | |
226 return obj; | |
227 } | |
228 }; | |
229 | |
230 private static final ArgConverter ARG_LONG = new ArgConverter() { | |
231 public Object convert(Object obj) { | |
232 if( obj instanceof Long ) | |
233 return obj; | |
234 if( obj instanceof Number ) { | |
235 Number n = (Number)obj; | |
236 long r = n.longValue(); | |
237 if( r==n.doubleValue() ) | |
238 return r; | |
239 } | |
240 else if( obj instanceof String ) { | |
241 String s = (String)obj; | |
242 try { | |
243 return Long.valueOf(s); | |
244 } catch(NumberFormatException e) {} | |
245 } | |
246 return obj; | |
247 } | |
248 }; | |
249 | |
250 private static final ArgConverter ARG_INTEGER = new ArgConverter() { | |
251 public Object convert(Object obj) { | |
252 if( obj instanceof Integer ) | |
253 return obj; | |
254 if( obj instanceof Number ) { | |
255 Number n = (Number)obj; | |
256 int r = n.intValue(); | |
257 if( r==n.doubleValue() ) | |
258 return r; | |
259 } | |
260 else if( obj instanceof String ) { | |
261 String s = (String)obj; | |
262 try { | |
263 return Integer.valueOf(s); | |
264 } catch(NumberFormatException e) {} | |
265 } | |
266 return obj; | |
267 } | |
268 }; | |
269 | |
270 private static final ArgConverter ARG_SHORT = new ArgConverter() { | |
271 public Object convert(Object obj) { | |
272 if( obj instanceof Short ) | |
273 return obj; | |
274 if( obj instanceof Number ) { | |
275 Number n = (Number)obj; | |
276 short r = n.shortValue(); | |
277 if( r==n.doubleValue() ) | |
278 return r; | |
279 } | |
280 else if( obj instanceof String ) { | |
281 String s = (String)obj; | |
282 try { | |
283 return Short.valueOf(s); | |
284 } catch(NumberFormatException e) {} | |
285 } | |
286 return obj; | |
287 } | |
288 }; | |
289 | |
290 private static final ArgConverter ARG_BYTE = new ArgConverter() { | |
291 public Object convert(Object obj) { | |
292 if( obj instanceof Byte ) | |
293 return obj; | |
294 if( obj instanceof Number ) { | |
295 Number n = (Number)obj; | |
296 byte r = n.byteValue(); | |
297 if( r==n.doubleValue() ) | |
298 return r; | |
299 } | |
300 else if( obj instanceof String ) { | |
301 String s = (String)obj; | |
302 try { | |
303 return Byte.valueOf(s); | |
304 } catch(NumberFormatException e) {} | |
305 } | |
306 return obj; | |
307 } | |
308 }; | |
309 | |
310 private static final ArgConverter ARG_TABLE = new ArgConverter() { | |
311 public Object convert(Object obj) { | |
312 if( obj instanceof List ) { | |
313 @SuppressWarnings("unchecked") | |
314 List<Object> list = (List<Object>)obj; | |
315 return new LuanTable(list); | |
316 } | |
317 if( obj instanceof Map ) { | |
318 @SuppressWarnings("unchecked") | |
319 Map<Object,Object> map = (Map<Object,Object>)obj; | |
320 return new LuanTable(map); | |
321 } | |
322 if( obj instanceof Set ) { | |
323 @SuppressWarnings("unchecked") | |
324 Set<Object> set = (Set<Object>)obj; | |
325 return new LuanTable(set); | |
326 } | |
327 return obj; | |
328 } | |
329 }; | |
330 | |
331 private static final ArgConverter ARG_MAP = new ArgConverter() { | |
332 public Object convert(Object obj) { | |
333 if( obj instanceof LuanTable ) { | |
334 LuanTable t = (LuanTable)obj; | |
335 return t.asMap(); | |
336 } | |
337 return obj; | |
338 } | |
339 }; | |
340 | |
341 private static final ArgConverter ARG_LIST = new ArgConverter() { | |
342 public Object convert(Object obj) { | |
343 if( obj instanceof LuanTable ) { | |
344 LuanTable t = (LuanTable)obj; | |
345 if( t.isList() ) | |
346 return t.asList(); | |
347 } | |
348 return obj; | |
349 } | |
350 }; | |
351 | |
352 private static final ArgConverter ARG_SET = new ArgConverter() { | |
353 public Object convert(Object obj) { | |
354 if( obj instanceof LuanTable ) { | |
355 LuanTable t = (LuanTable)obj; | |
356 if( t.isSet() ) | |
357 return t.asSet(); | |
358 } | |
359 return obj; | |
360 } | |
361 }; | |
362 | |
363 private static boolean takesLuaState(JavaMethod m) { | |
364 Class<?>[] paramTypes = m.getParameterTypes(); | |
365 return paramTypes.length > 0 && paramTypes[0].equals(LuanState.class); | |
366 } | |
367 | |
368 private static ArgConverter[] getArgConverters(boolean takesLuaState,JavaMethod m) { | |
369 final boolean isVarArgs = m.isVarArgs(); | |
370 Class<?>[] paramTypes = m.getParameterTypes(); | |
371 if( takesLuaState ) { | |
372 Class<?>[] t = new Class<?>[paramTypes.length-1]; | |
373 System.arraycopy(paramTypes,1,t,0,t.length); | |
374 paramTypes = t; | |
375 } | |
376 ArgConverter[] a = new ArgConverter[paramTypes.length]; | |
377 for( int i=0; i<a.length; i++ ) { | |
378 Class<?> paramType = paramTypes[i]; | |
379 if( isVarArgs && i == a.length-1 ) | |
380 paramType = paramType.getComponentType(); | |
381 a[i] = getArgConverter(paramType); | |
382 } | |
383 return a; | |
384 } | |
385 | |
386 private static ArgConverter getArgConverter(Class<?> cls) { | |
387 if( cls == Double.TYPE || cls.equals(Double.class) ) | |
388 return ARG_DOUBLE; | |
389 if( cls == Float.TYPE || cls.equals(Float.class) ) | |
390 return ARG_FLOAT; | |
391 if( cls == Long.TYPE || cls.equals(Long.class) ) | |
392 return ARG_LONG; | |
393 if( cls == Integer.TYPE || cls.equals(Integer.class) ) | |
394 return ARG_INTEGER; | |
395 if( cls == Short.TYPE || cls.equals(Short.class) ) | |
396 return ARG_SHORT; | |
397 if( cls == Byte.TYPE || cls.equals(Byte.class) ) | |
398 return ARG_BYTE; | |
399 if( cls.equals(LuanTable.class) ) | |
400 return ARG_TABLE; | |
401 if( cls.equals(Map.class) ) | |
402 return ARG_MAP; | |
403 if( cls.equals(List.class) ) | |
404 return ARG_LIST; | |
405 if( cls.equals(Set.class) ) | |
406 return ARG_SET; | |
407 return ARG_SAME; | |
408 } | |
409 | |
410 | |
411 | |
412 private static abstract class JavaMethod { | |
413 abstract boolean isVarArgs(); | |
414 abstract Class<?>[] getParameterTypes(); | |
415 abstract Object invoke(Object obj,Object... args) | |
416 throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException; | |
417 abstract Class<?> getReturnType(); | |
418 | |
419 static JavaMethod of(final Method m) { | |
420 return new JavaMethod() { | |
421 @Override boolean isVarArgs() { | |
422 return m.isVarArgs(); | |
423 } | |
424 @Override Class<?>[] getParameterTypes() { | |
425 return m.getParameterTypes(); | |
426 } | |
427 @Override Object invoke(Object obj,Object... args) | |
428 throws IllegalAccessException, IllegalArgumentException, InvocationTargetException | |
429 { | |
430 return m.invoke(obj,args); | |
431 } | |
432 @Override Class<?> getReturnType() { | |
433 return m.getReturnType(); | |
434 } | |
435 @Override public String toString() { | |
436 return m.toString(); | |
437 } | |
438 }; | |
439 } | |
440 | |
441 static JavaMethod of(final Constructor c) { | |
442 return new JavaMethod() { | |
443 @Override boolean isVarArgs() { | |
444 return c.isVarArgs(); | |
445 } | |
446 @Override Class<?>[] getParameterTypes() { | |
447 return c.getParameterTypes(); | |
448 } | |
449 @Override Object invoke(Object obj,Object... args) | |
450 throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException | |
451 { | |
452 return c.newInstance(args); | |
453 } | |
454 @Override Class<?> getReturnType() { | |
455 return c.getDeclaringClass(); | |
456 } | |
457 @Override public String toString() { | |
458 return c.toString(); | |
459 } | |
460 }; | |
461 } | |
462 | |
463 } | |
464 | |
465 } |