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