1 /*
2  * $Id: LuaObject.java,v 1.6 2006/12/22 14:06:40 thiago Exp $
3  * Copyright (C) 2003-2007 Kepler Project.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 package org.keplerproject.luajava;
26 
27 import java.lang.reflect.InvocationHandler;
28 import java.lang.reflect.Proxy;
29 import java.util.StringTokenizer;
30 
31 /**
32  * This class represents a Lua object of any type. A LuaObject is constructed by a {@link LuaState} object using one of
33  * the four methods:
34  * <ul>
35  * <li>{@link LuaState#getLuaObject(String globalName)}</li>
36  * <li>{@link LuaState#getLuaObject(LuaObject parent, String name)}</li>
37  * <li>{@link LuaState#getLuaObject(LuaObject parent, Number name)}</li>
38  * <li>{@link LuaState#getLuaObject(LuaObject parent, LuaObject name)}</li>
39  * <li>{@link LuaState#getLuaObject(int index)}</li>
40  * </ul>
41  * The LuaObject will represent only the object itself, not a variable or a stack index, so when you change a string,
42  * remember that strings are immutable objects in Lua, and the LuaObject you have will represent the old one.
43  *
44  * <h2>Proxies</h2>
45  *
46  * LuaJava allows you to implement a class in Lua, like said before. If you want to create this proxy from Java, you
47  * should have a LuaObject representing the table that has the functions that implement the interface. From this
48  * LuaObject you can call the <code>createProxy(String implements)</code>. This method receives the string with the
49  * name of the interfaces implemented by the object separated by comma.
50  *
51  * @author Rizzato
52  * @author Thiago Ponte
53  */
54 public class LuaObject
55 {
56 	protected Integer ref;
57 
58 	protected LuaState L;
59 
60 	/**
61 	 * Creates a reference to an object in the variable globalName
62 	 *
63 	 * @param L
64 	 * @param globalName
65 	 */
LuaObject(LuaState L, String globalName)66 	protected LuaObject(LuaState L, String globalName)
67 	{
68 		synchronized (L)
69 		{
70 			this.L = L;
71 			L.getGlobal(globalName);
72 			registerValue(-1);
73 			L.pop(1);
74 		}
75 	}
76 
77 	/**
78 	 * Creates a reference to an object inside another object
79 	 *
80 	 * @param parent
81 	 *            The Lua Table or Userdata that contains the Field.
82 	 * @param name
83 	 *            The name that index the field
84 	 */
LuaObject(LuaObject parent, String name)85 	protected LuaObject(LuaObject parent, String name) throws LuaException
86 	{
87 		synchronized (parent.getLuaState())
88 		{
89 			this.L = parent.getLuaState();
90 
91 			if (!parent.isTable() && !parent.isUserdata())
92 			{
93 				throw new LuaException("Object parent should be a table or userdata .");
94 			}
95 
96 			parent.push();
97 			L.pushString(name);
98 			L.getTable(-2);
99 			L.remove(-2);
100 			registerValue(-1);
101 			L.pop(1);
102 		}
103 	}
104 
105 	/**
106 	 * This constructor creates a LuaObject from a table that is indexed by a number.
107 	 *
108 	 * @param parent
109 	 *            The Lua Table or Userdata that contains the Field.
110 	 * @param name
111 	 *            The name (number) that index the field
112 	 * @throws LuaException
113 	 *             When the parent object isn't a Table or Userdata
114 	 */
LuaObject(LuaObject parent, Number name)115 	protected LuaObject(LuaObject parent, Number name) throws LuaException
116 	{
117 		synchronized (parent.getLuaState())
118 		{
119 			this.L = parent.getLuaState();
120 			if (!parent.isTable() && !parent.isUserdata())
121 				throw new LuaException("Object parent should be a table or userdata .");
122 
123 			parent.push();
124 			L.pushNumber(name.doubleValue());
125 			L.getTable(-2);
126 			L.remove(-2);
127 			registerValue(-1);
128 			L.pop(1);
129 		}
130 	}
131 
132 	/**
133 	 * This constructor creates a LuaObject from a table that is indexed by a LuaObject.
134 	 *
135 	 * @param parent
136 	 *            The Lua Table or Userdata that contains the Field.
137 	 * @param name
138 	 *            The name (LuaObject) that index the field
139 	 * @throws LuaException
140 	 *             When the parent object isn't a Table or Userdata
141 	 */
LuaObject(LuaObject parent, LuaObject name)142 	protected LuaObject(LuaObject parent, LuaObject name) throws LuaException
143 	{
144 		if (parent.getLuaState() != name.getLuaState())
145 			throw new LuaException("LuaStates must be the same!");
146 		synchronized (parent.getLuaState())
147 		{
148 			if (!parent.isTable() && !parent.isUserdata())
149 				throw new LuaException("Object parent should be a table or userdata .");
150 
151 			this.L = parent.getLuaState();
152 
153 			parent.push();
154 			name.push();
155 			L.getTable(-2);
156 			L.remove(-2);
157 			registerValue(-1);
158 			L.pop(1);
159 		}
160 	}
161 
162 	/**
163 	 * Creates a reference to an object in the given index of the stack
164 	 *
165 	 * @param L
166 	 * @param index
167 	 *            of the object on the lua stack
168 	 */
LuaObject(LuaState L, int index)169 	protected LuaObject(LuaState L, int index)
170 	{
171 		synchronized (L)
172 		{
173 			this.L = L;
174 
175 			registerValue(index);
176 		}
177 	}
178 
179 	/**
180 	 * Gets the Object's State
181 	 */
getLuaState()182 	public LuaState getLuaState()
183 	{
184 		return L;
185 	}
186 
187 	/**
188 	 * Creates the reference to the object in the registry table
189 	 *
190 	 * @param index
191 	 *            of the object on the lua stack
192 	 */
registerValue(int index)193 	private void registerValue(int index)
194 	{
195 		synchronized (L)
196 		{
197 			L.pushValue(index);
198 			int key = L.Lref(LuaState.LUA_REGISTRYINDEX.intValue());
199 			ref = new Integer(key);
200 		}
201 	}
202 
finalize()203 	protected void finalize()
204 	{
205 		try
206 		{
207 			synchronized (L)
208 			{
209 				if (L.getCPtrPeer() != 0)
210 					L.LunRef(LuaState.LUA_REGISTRYINDEX.intValue(), ref.intValue());
211 			}
212 		}
213 		catch (Exception e)
214 		{
215 			System.err.println("Unable to release object " + ref);
216 		}
217 	}
218 
219 	/**
220 	 * Pushes the object represented by <code>this<code> into L's stack
221 	 */
push()222 	public void push()
223 	{
224 		L.rawGetI(LuaState.LUA_REGISTRYINDEX.intValue(), ref.intValue());
225 	}
226 
isNil()227 	public boolean isNil()
228 	{
229 		synchronized (L)
230 		{
231 			push();
232 			boolean bool = L.isNil(-1);
233 			L.pop(1);
234 			return bool;
235 		}
236 	}
237 
isBoolean()238 	public boolean isBoolean()
239 	{
240 		synchronized (L)
241 		{
242 			push();
243 			boolean bool = L.isBoolean(-1);
244 			L.pop(1);
245 			return bool;
246 		}
247 	}
248 
isNumber()249 	public boolean isNumber()
250 	{
251 		synchronized (L)
252 		{
253 			push();
254 			boolean bool = L.isNumber(-1);
255 			L.pop(1);
256 			return bool;
257 		}
258 	}
259 
isString()260 	public boolean isString()
261 	{
262 		synchronized (L)
263 		{
264 			push();
265 			boolean bool = L.isString(-1);
266 			L.pop(1);
267 			return bool;
268 		}
269 	}
270 
isFunction()271 	public boolean isFunction()
272 	{
273 		synchronized (L)
274 		{
275 			push();
276 			boolean bool = L.isFunction(-1);
277 			L.pop(1);
278 			return bool;
279 		}
280 	}
281 
isJavaObject()282 	public boolean isJavaObject()
283 	{
284 		synchronized (L)
285 		{
286 			push();
287 			boolean bool = L.isObject(-1);
288 			L.pop(1);
289 			return bool;
290 		}
291 	}
292 
isJavaFunction()293 	public boolean isJavaFunction()
294 	{
295 		synchronized (L)
296 		{
297 			push();
298 			boolean bool = L.isJavaFunction(-1);
299 			L.pop(1);
300 			return bool;
301 		}
302 	}
303 
isTable()304 	public boolean isTable()
305 	{
306 		synchronized (L)
307 		{
308 			push();
309 			boolean bool = L.isTable(-1);
310 			L.pop(1);
311 			return bool;
312 		}
313 	}
314 
isUserdata()315 	public boolean isUserdata()
316 	{
317 		synchronized (L)
318 		{
319 			push();
320 			boolean bool = L.isUserdata(-1);
321 			L.pop(1);
322 			return bool;
323 		}
324 	}
325 
type()326 	public int type()
327 	{
328 		synchronized (L)
329 		{
330 			push();
331 			int type = L.type(-1);
332 			L.pop(1);
333 			return type;
334 		}
335 	}
336 
getBoolean()337 	public boolean getBoolean()
338 	{
339 		synchronized (L)
340 		{
341 			push();
342 			boolean bool = L.toBoolean(-1);
343 			L.pop(1);
344 			return bool;
345 		}
346 	}
347 
getNumber()348 	public double getNumber()
349 	{
350 		synchronized (L)
351 		{
352 			push();
353 			double db = L.toNumber(-1);
354 			L.pop(1);
355 			return db;
356 		}
357 	}
358 
getString()359 	public String getString()
360 	{
361 		synchronized (L)
362 		{
363 			push();
364 			String str = L.toString(-1);
365 			L.pop(1);
366 			return str;
367 		}
368 	}
369 
getObject()370 	public Object getObject() throws LuaException
371 	{
372 		synchronized (L)
373 		{
374 			push();
375 			Object obj = L.getObjectFromUserdata(-1);
376 			L.pop(1);
377 			return obj;
378 		}
379 	}
380 
381 	/**
382 	 * If <code>this<code> is a table or userdata tries to set
383 	 * a field value.
384 	 */
getField(String field)385 	public LuaObject getField(String field) throws LuaException
386 	{
387 		return L.getLuaObject(this, field);
388 	}
389 
390 	/**
391 	 * Calls the object represented by <code>this</code> using Lua function pcall.
392 	 *
393 	 * @param args -
394 	 *            Call arguments
395 	 * @param nres -
396 	 *            Number of objects returned
397 	 * @return Object[] - Returned Objects
398 	 * @throws LuaException
399 	 */
call(Object[] args, int nres)400 	public Object[] call(Object[] args, int nres) throws LuaException
401 	{
402 		synchronized (L)
403 		{
404 			if (!isFunction() && !isTable() && !isUserdata())
405 				throw new LuaException("Invalid object. Not a function, table or userdata .");
406 
407 			int top = L.getTop();
408 			push();
409 			int nargs;
410 			if (args != null)
411 			{
412 				nargs = args.length;
413 				for (int i = 0; i < nargs; i++)
414 				{
415 					Object obj = args[i];
416 					L.pushObjectValue(obj);
417 				}
418 			}
419 			else
420 				nargs = 0;
421 
422 			int err = L.pcall(nargs, nres, 0);
423 
424 			if (err != 0)
425 			{
426 				String str;
427 				if (L.isString(-1))
428 				{
429 					str = L.toString(-1);
430 					L.pop(1);
431 				}
432 				else
433 					str = "";
434 
435 				if (err == LuaState.LUA_ERRRUN.intValue())
436 				{
437 					str = "Runtime error. " + str;
438 				}
439 				else if (err == LuaState.LUA_ERRMEM.intValue())
440 				{
441 					str = "Memory allocation error. " + str;
442 				}
443 				else if (err == LuaState.LUA_ERRERR.intValue())
444 				{
445 					str = "Error while running the error handler function. " + str;
446 				}
447 				else
448 				{
449 					str = "Lua Error code " + err + ". " + str;
450 				}
451 
452 				throw new LuaException(str);
453 			}
454 
455 			if (nres == LuaState.LUA_MULTRET.intValue())
456 				nres = L.getTop() - top;
457 			if (L.getTop() - top < nres)
458 			{
459 				throw new LuaException("Invalid Number of Results .");
460 			}
461 
462 			Object[] res = new Object[nres];
463 
464 			for (int i = nres; i > 0; i--)
465 			{
466 				res[i - 1] = L.toJavaObject(-1);
467 				L.pop(1);
468 			}
469 			return res;
470 		}
471 	}
472 
473 	/**
474 	 * Calls the object represented by <code>this</code> using Lua function pcall. Returns 1 object
475 	 *
476 	 * @param args -
477 	 *            Call arguments
478 	 * @return Object - Returned Object
479 	 * @throws LuaException
480 	 */
call(Object[] args)481 	public Object call(Object[] args) throws LuaException
482 	{
483 		return call(args, 1)[0];
484 	}
485 
toString()486 	public String toString()
487 	{
488 		synchronized (L)
489 		{
490 			try
491 			{
492 				if (isNil())
493 					return "nil";
494 				else if (isBoolean())
495 					return String.valueOf(getBoolean());
496 				else if (isNumber())
497 					return String.valueOf(getNumber());
498 				else if (isString())
499 					return getString();
500 				else if (isFunction())
501 					return "Lua Function";
502 				else if (isJavaObject())
503 					return getObject().toString();
504 				else if (isUserdata())
505 					return "Userdata";
506 				else if (isTable())
507 					return "Lua Table";
508 				else if (isJavaFunction())
509 					return "Java Function";
510 				else
511 					return null;
512 			}
513 			catch (LuaException e)
514 			{
515 				return null;
516 			}
517 		}
518 	}
519 
520 	/**
521 	 * Function that creates a java proxy to the object represented by <code>this</code>
522 	 *
523 	 * @param implem
524 	 *            Interfaces that are implemented, separated by <code>,</code>
525 	 */
createProxy(String implem)526 	public Object createProxy(String implem) throws ClassNotFoundException, LuaException
527 	{
528 		synchronized (L)
529 		{
530 			if (!isTable())
531 				throw new LuaException("Invalid Object. Must be Table.");
532 
533 			StringTokenizer st = new StringTokenizer(implem, ",");
534 			Class[] interfaces = new Class[st.countTokens()];
535 			for (int i = 0; st.hasMoreTokens(); i++)
536 				interfaces[i] = Class.forName(st.nextToken());
537 
538 			InvocationHandler handler = new LuaInvocationHandler(this);
539 
540 			return Proxy.newProxyInstance(this.getClass().getClassLoader(), interfaces, handler);
541 		}
542 	}
543 }
544