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