comparison src/luan/modules/JavaLuan.java @ 168:ebe9db183eb7

rename *Lib.java to *Luan.java git-svn-id: https://luan-java.googlecode.com/svn/trunk@169 21e917c8-12df-6dd8-5cb6-c86387c605b9
author fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9>
date Sun, 22 Jun 2014 04:42:07 +0000
parents src/luan/modules/JavaLib.java@4c0131c2b650
children
comparison
equal deleted inserted replaced
167:4c0131c2b650 168:ebe9db183eb7
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 }