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 }