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 }