Mercurial Hosting > luan
comparison core/src/luan/modules/JavaLuan.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/modules/JavaLuan.java@ebe9db183eb7 |
children | 24ede40ee0aa |
comparison
equal
deleted
inserted
replaced
170:7c792a328a83 | 171:3dcb0f9bee82 |
---|---|
1 package luan.modules; | |
2 | |
3 import java.lang.reflect.Array; | |
4 import java.lang.reflect.AccessibleObject; | |
5 import java.lang.reflect.Member; | |
6 import java.lang.reflect.Field; | |
7 import java.lang.reflect.Method; | |
8 import java.lang.reflect.Constructor; | |
9 import java.lang.reflect.Modifier; | |
10 import java.lang.reflect.InvocationHandler; | |
11 import java.lang.reflect.Proxy; | |
12 import java.util.Map; | |
13 import java.util.HashMap; | |
14 import java.util.List; | |
15 import java.util.ArrayList; | |
16 import java.util.Iterator; | |
17 import java.util.Collections; | |
18 import java.util.Arrays; | |
19 import luan.Luan; | |
20 import luan.LuanState; | |
21 import luan.LuanTable; | |
22 import luan.MetatableGetter; | |
23 import luan.LuanException; | |
24 import luan.LuanFunction; | |
25 import luan.LuanJavaFunction; | |
26 import luan.LuanElement; | |
27 | |
28 | |
29 public final class JavaLuan { | |
30 | |
31 public static final LuanFunction LOADER = new LuanFunction() { | |
32 @Override public Object call(LuanState luan,Object[] args) { | |
33 luan.addMetatableGetter(mg); | |
34 LuanTable module = new LuanTable(); | |
35 try { | |
36 module.put( "class", new LuanJavaFunction(JavaLuan.class.getMethod("getClass",LuanState.class,String.class),null) ); | |
37 add( module, "proxy", LuanState.class, Static.class, LuanTable.class, Object.class ); | |
38 } catch(NoSuchMethodException e) { | |
39 throw new RuntimeException(e); | |
40 } | |
41 luan.searchers().add(javaSearcher); | |
42 return module; | |
43 } | |
44 }; | |
45 | |
46 public static final LuanFunction javaSearcher = new LuanFunction() { | |
47 @Override public Object call(LuanState luan,Object[] args) throws LuanException { | |
48 String modName = (String)args[0]; | |
49 final Static s = JavaLuan.getClass(luan,modName); | |
50 if( s==null ) | |
51 return null; | |
52 LuanFunction loader = new LuanFunction() { | |
53 @Override public Object call(LuanState luan,Object[] args) { | |
54 return s; | |
55 } | |
56 }; | |
57 return loader; | |
58 } | |
59 }; | |
60 | |
61 private static void add(LuanTable t,String method,Class<?>... parameterTypes) { | |
62 try { | |
63 t.put( method, new LuanJavaFunction(JavaLuan.class.getMethod(method,parameterTypes),null) ); | |
64 } catch(NoSuchMethodException e) { | |
65 throw new RuntimeException(e); | |
66 } | |
67 } | |
68 | |
69 private static final LuanTable mt = new LuanTable(); | |
70 static { | |
71 add( mt, "__index", LuanState.class, Object.class, Object.class ); | |
72 add( mt, "__newindex", LuanState.class, Object.class, Object.class, Object.class ); | |
73 } | |
74 | |
75 private static final MetatableGetter mg = new MetatableGetter() { | |
76 public LuanTable getMetatable(Object obj) { | |
77 if( obj==null ) | |
78 return null; | |
79 return mt; | |
80 } | |
81 }; | |
82 | |
83 public static Object __index(LuanState luan,Object obj,Object key) throws LuanException { | |
84 if( obj instanceof Static ) { | |
85 if( key instanceof String ) { | |
86 String name = (String)key; | |
87 Static st = (Static)obj; | |
88 Class cls = st.cls; | |
89 if( "class".equals(name) ) { | |
90 return cls; | |
91 } else if( "new".equals(name) ) { | |
92 Constructor<?>[] constructors = cls.getConstructors(); | |
93 if( constructors.length > 0 ) { | |
94 if( constructors.length==1 ) { | |
95 return new LuanJavaFunction(constructors[0],null); | |
96 } else { | |
97 List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>(); | |
98 for( Constructor constructor : constructors ) { | |
99 fns.add(new LuanJavaFunction(constructor,null)); | |
100 } | |
101 return new AmbiguousJavaFunction(fns); | |
102 } | |
103 } | |
104 } else if( "assert".equals(name) ) { | |
105 return new LuanJavaFunction(assertClass,new AssertClass(cls)); | |
106 } else { | |
107 List<Member> members = getStaticMembers(cls,name); | |
108 if( !members.isEmpty() ) { | |
109 return member(null,members); | |
110 } | |
111 } | |
112 } | |
113 throw luan.exception("invalid member '"+key+"' for: "+obj); | |
114 } | |
115 Class cls = obj.getClass(); | |
116 if( cls.isArray() ) { | |
117 if( "length".equals(key) ) { | |
118 return Array.getLength(obj); | |
119 } | |
120 Integer i = Luan.asInteger(key); | |
121 if( i != null ) { | |
122 return Array.get(obj,i); | |
123 } | |
124 throw luan.exception("invalid member '"+key+"' for java array: "+obj); | |
125 } | |
126 if( key instanceof String ) { | |
127 String name = (String)key; | |
128 if( "instanceof".equals(name) ) { | |
129 return new LuanJavaFunction(instanceOf,new InstanceOf(obj)); | |
130 } else { | |
131 List<Member> members = getMembers(cls,name); | |
132 if( !members.isEmpty() ) { | |
133 return member(obj,members); | |
134 } | |
135 } | |
136 } | |
137 // throw luan.exception("invalid member '"+key+"' for java object: "+obj); | |
138 return null; | |
139 } | |
140 | |
141 private static Object member(Object obj,List<Member> members) throws LuanException { | |
142 try { | |
143 if( members.size()==1 ) { | |
144 Member member = members.get(0); | |
145 if( member instanceof Static ) { | |
146 return member; | |
147 } else if( member instanceof Field ) { | |
148 Field field = (Field)member; | |
149 Object rtn = field.get(obj); | |
150 return rtn instanceof Object[] ? Arrays.asList((Object[])rtn) : rtn; | |
151 } else { | |
152 Method method = (Method)member; | |
153 return new LuanJavaFunction(method,obj); | |
154 } | |
155 } else { | |
156 List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>(); | |
157 for( Member member : members ) { | |
158 Method method = (Method)member; | |
159 fns.add(new LuanJavaFunction(method,obj)); | |
160 } | |
161 return new AmbiguousJavaFunction(fns); | |
162 } | |
163 } catch(IllegalAccessException e) { | |
164 throw new RuntimeException(e); | |
165 } | |
166 } | |
167 | |
168 public static void __newindex(LuanState luan,Object obj,Object key,Object value) throws LuanException { | |
169 if( obj instanceof Static ) { | |
170 if( key instanceof String ) { | |
171 String name = (String)key; | |
172 Static st = (Static)obj; | |
173 Class cls = st.cls; | |
174 List<Member> members = getStaticMembers(cls,name); | |
175 if( !members.isEmpty() ) { | |
176 if( members.size() != 1 ) | |
177 throw new RuntimeException("not field '"+name+"' of "+obj); | |
178 setMember(obj,members,value); | |
179 return; | |
180 } | |
181 } | |
182 throw luan.exception("invalid member '"+key+"' for: "+obj); | |
183 } | |
184 Class cls = obj.getClass(); | |
185 if( cls.isArray() ) { | |
186 Integer i = Luan.asInteger(key); | |
187 if( i != null ) { | |
188 Array.set(obj,i,value); | |
189 return; | |
190 } | |
191 throw luan.exception("invalid member '"+key+"' for java array: "+obj); | |
192 } | |
193 if( key instanceof String ) { | |
194 String name = (String)key; | |
195 List<Member> members = getMembers(cls,name); | |
196 if( !members.isEmpty() ) { | |
197 if( members.size() != 1 ) | |
198 throw new RuntimeException("not field '"+name+"' of "+obj); | |
199 setMember(obj,members,value); | |
200 return; | |
201 } | |
202 } | |
203 throw luan.exception("invalid member '"+key+"' for java object: "+obj); | |
204 } | |
205 | |
206 private static void setMember(Object obj,List<Member> members,Object value) { | |
207 Field field = (Field)members.get(0); | |
208 try { | |
209 try { | |
210 field.set(obj,value); | |
211 } catch(IllegalArgumentException e) { | |
212 Class cls = field.getType(); | |
213 if( value instanceof Number ) { | |
214 Number n = (Number)value; | |
215 if( cls.equals(Integer.TYPE) || cls.equals(Integer.class) ) { | |
216 int r = n.intValue(); | |
217 if( r==n.doubleValue() ) { | |
218 field.setInt(obj,r); | |
219 return; | |
220 } | |
221 } | |
222 } | |
223 throw e; | |
224 } | |
225 } catch(IllegalAccessException e) { | |
226 throw new RuntimeException(e); | |
227 } | |
228 } | |
229 | |
230 public static boolean privateAccess = false; | |
231 private static Map<Class,Map<String,List<Member>>> memberMap = new HashMap<Class,Map<String,List<Member>>>(); | |
232 | |
233 private static synchronized List<Member> getMembers(Class cls,String name) { | |
234 Map<String,List<Member>> clsMap = memberMap.get(cls); | |
235 if( clsMap == null ) { | |
236 clsMap = new HashMap<String,List<Member>>(); | |
237 for( Class c : cls.getClasses() ) { | |
238 String s = c.getSimpleName(); | |
239 List<Member> list = new ArrayList<Member>(); | |
240 clsMap.put(s,list); | |
241 list.add(new Static(c)); | |
242 } | |
243 for( Field field : cls.getFields() ) { | |
244 String s = field.getName(); | |
245 try { | |
246 if( !cls.getField(s).equals(field) ) | |
247 continue; // not accessible | |
248 } catch(NoSuchFieldException e) { | |
249 throw new RuntimeException(e); | |
250 } | |
251 List<Member> list = new ArrayList<Member>(); | |
252 clsMap.put(s,list); | |
253 list.add(field); | |
254 } | |
255 for( Method method : cls.getMethods() ) { | |
256 String s = method.getName(); | |
257 List<Member> list = clsMap.get(s); | |
258 if( list == null || !(list.get(0) instanceof Method) ) { | |
259 list = new ArrayList<Member>(); | |
260 clsMap.put(s,list); | |
261 } | |
262 list.add(method); | |
263 } | |
264 if( privateAccess ) { | |
265 for( Method method : cls.getDeclaredMethods() ) { | |
266 String s = method.getName(); | |
267 List<Member> list = clsMap.get(s); | |
268 if( list == null ) { | |
269 list = new ArrayList<Member>(); | |
270 clsMap.put(s,list); | |
271 } else if( !(list.get(0) instanceof Method) ) | |
272 continue; | |
273 if( !list.contains(method) ) { | |
274 list.add(method); | |
275 } | |
276 } | |
277 for( Field field : cls.getDeclaredFields() ) { | |
278 String s = field.getName(); | |
279 List<Member> list = clsMap.get(s); | |
280 if( list == null ) { | |
281 list = new ArrayList<Member>(); | |
282 clsMap.put(s,list); | |
283 list.add(field); | |
284 } | |
285 } | |
286 } | |
287 for( List<Member> members : clsMap.values() ) { | |
288 for( Member m : members ) { | |
289 if( m instanceof AccessibleObject ) | |
290 ((AccessibleObject)m).setAccessible(true); | |
291 } | |
292 } | |
293 memberMap.put(cls,clsMap); | |
294 } | |
295 List<Member> rtn = clsMap.get(name); | |
296 if( rtn==null ) | |
297 rtn = Collections.emptyList(); | |
298 return rtn; | |
299 } | |
300 | |
301 private static synchronized List<Member> getStaticMembers(Class cls,String name) { | |
302 List<Member> staticMembers = new ArrayList<Member>(); | |
303 for( Member m : getMembers(cls,name) ) { | |
304 if( Modifier.isStatic(m.getModifiers()) ) | |
305 staticMembers.add(m); | |
306 } | |
307 return staticMembers; | |
308 } | |
309 | |
310 static final class Static implements Member { | |
311 final Class cls; | |
312 | |
313 Static(Class cls) { | |
314 this.cls = cls; | |
315 } | |
316 | |
317 @Override public String toString() { | |
318 return cls.toString(); | |
319 } | |
320 | |
321 @Override public Class<?> getDeclaringClass() { | |
322 return cls.getDeclaringClass(); | |
323 } | |
324 | |
325 @Override public String getName() { | |
326 return cls.getName(); | |
327 } | |
328 | |
329 @Override public int getModifiers() { | |
330 return cls.getModifiers(); | |
331 } | |
332 | |
333 @Override public boolean isSynthetic() { | |
334 return cls.isSynthetic(); | |
335 } | |
336 } | |
337 | |
338 public static Static getClass(LuanState luan,String name) throws LuanException { | |
339 Class cls; | |
340 try { | |
341 cls = Class.forName(name); | |
342 } catch(ClassNotFoundException e) { | |
343 try { | |
344 cls = Thread.currentThread().getContextClassLoader().loadClass(name); | |
345 } catch(ClassNotFoundException e2) { | |
346 return null; | |
347 } | |
348 } | |
349 return new Static(cls); | |
350 } | |
351 /* | |
352 public static void importClass(LuanState luan,String name) throws LuanException { | |
353 luan.currentEnvironment().put( name.substring(name.lastIndexOf('.')+1), getClass(luan,name) ); | |
354 } | |
355 */ | |
356 static class AmbiguousJavaFunction extends LuanFunction { | |
357 private final Map<Integer,List<LuanJavaFunction>> fnMap = new HashMap<Integer,List<LuanJavaFunction>>(); | |
358 | |
359 AmbiguousJavaFunction(List<LuanJavaFunction> fns) { | |
360 for( LuanJavaFunction fn : fns ) { | |
361 Integer n = fn.getParameterTypes().length; | |
362 List<LuanJavaFunction> list = fnMap.get(n); | |
363 if( list==null ) { | |
364 list = new ArrayList<LuanJavaFunction>(); | |
365 fnMap.put(n,list); | |
366 } | |
367 list.add(fn); | |
368 } | |
369 } | |
370 | |
371 @Override public Object call(LuanState luan,Object[] args) throws LuanException { | |
372 for( LuanJavaFunction fn : fnMap.get(args.length) ) { | |
373 try { | |
374 return fn.rawCall(luan,args); | |
375 } catch(IllegalArgumentException e) {} | |
376 } | |
377 throw luan.exception("no method matched args"); | |
378 } | |
379 } | |
380 | |
381 private static class InstanceOf { | |
382 private final Object obj; | |
383 | |
384 InstanceOf(Object obj) { | |
385 this.obj = obj; | |
386 } | |
387 | |
388 public boolean instanceOf(Static st) { | |
389 return st.cls.isInstance(obj); | |
390 } | |
391 } | |
392 private static final Method instanceOf; | |
393 static { | |
394 try { | |
395 instanceOf = InstanceOf.class.getMethod("instanceOf",Static.class); | |
396 instanceOf.setAccessible(true); | |
397 } catch(NoSuchMethodException e) { | |
398 throw new RuntimeException(e); | |
399 } | |
400 } | |
401 | |
402 | |
403 private static class AssertClass { | |
404 private final Class cls; | |
405 | |
406 AssertClass(Class cls) { | |
407 this.cls = cls; | |
408 } | |
409 | |
410 public Object assertClass(LuanState luan,Object v) throws LuanException { | |
411 if( !cls.isInstance(v) ) { | |
412 String got = v.getClass().getSimpleName(); | |
413 String expected = cls.getSimpleName(); | |
414 throw luan.exception("bad argument #1 ("+expected+" expected, got "+got+")"); | |
415 } | |
416 return v; | |
417 } | |
418 } | |
419 private static final Method assertClass; | |
420 static { | |
421 try { | |
422 assertClass = AssertClass.class.getMethod("assertClass",LuanState.class,Object.class); | |
423 assertClass.setAccessible(true); | |
424 } catch(NoSuchMethodException e) { | |
425 throw new RuntimeException(e); | |
426 } | |
427 } | |
428 | |
429 | |
430 public static Object proxy(final LuanState luan,Static st,final LuanTable t,final Object base) throws LuanException { | |
431 return Proxy.newProxyInstance( | |
432 st.cls.getClassLoader(), | |
433 new Class[]{st.cls}, | |
434 new InvocationHandler() { | |
435 public Object invoke(Object proxy,Method method, Object[] args) | |
436 throws Throwable | |
437 { | |
438 if( args==null ) | |
439 args = new Object[0]; | |
440 String name = method.getName(); | |
441 Object fnObj = t.get(name); | |
442 if( fnObj==null && base!=null ) | |
443 return method.invoke(base,args); | |
444 LuanFunction fn = luan.checkFunction(fnObj); | |
445 return Luan.first(luan.call(fn,name,args)); | |
446 } | |
447 } | |
448 ); | |
449 } | |
450 } |