001: /*
002: * $Id: LuaObject.java,v 1.6 2006/12/22 14:06:40 thiago Exp $
003: * Copyright (C) 2003-2007 Kepler Project.
004: *
005: * Permission is hereby granted, free of charge, to any person obtaining
006: * a copy of this software and associated documentation files (the
007: * "Software"), to deal in the Software without restriction, including
008: * without limitation the rights to use, copy, modify, merge, publish,
009: * distribute, sublicense, and/or sell copies of the Software, and to
010: * permit persons to whom the Software is furnished to do so, subject to
011: * the following conditions:
012: *
013: * The above copyright notice and this permission notice shall be
014: * included in all copies or substantial portions of the Software.
015: *
016: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
017: * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
018: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
019: * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
020: * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
021: * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
022: * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
023: */
024:
025: package org.keplerproject.luajava;
026:
027: import java.lang.reflect.InvocationHandler;
028: import java.lang.reflect.Proxy;
029: import java.util.StringTokenizer;
030:
031: /**
032: * This class represents a Lua object of any type. A LuaObject is constructed by a {@link LuaState} object using one of
033: * the four methods:
034: * <ul>
035: * <li>{@link LuaState#getLuaObject(String globalName)}</li>
036: * <li>{@link LuaState#getLuaObject(LuaObject parent, String name)}</li>
037: * <li>{@link LuaState#getLuaObject(LuaObject parent, Number name)}</li>
038: * <li>{@link LuaState#getLuaObject(LuaObject parent, LuaObject name)}</li>
039: * <li>{@link LuaState#getLuaObject(int index)}</li>
040: * </ul>
041: * The LuaObject will represent only the object itself, not a variable or a stack index, so when you change a string,
042: * remember that strings are immutable objects in Lua, and the LuaObject you have will represent the old one.
043: *
044: * <h2>Proxies</h2>
045: *
046: * LuaJava allows you to implement a class in Lua, like said before. If you want to create this proxy from Java, you
047: * should have a LuaObject representing the table that has the functions that implement the interface. From this
048: * LuaObject you can call the <code>createProxy(String implements)</code>. This method receives the string with the
049: * name of the interfaces implemented by the object separated by comma.
050: *
051: * @author Rizzato
052: * @author Thiago Ponte
053: */
054: public class LuaObject {
055: protected Integer ref;
056:
057: protected LuaState L;
058:
059: /**
060: * Creates a reference to an object in the variable globalName
061: *
062: * @param L
063: * @param globalName
064: */
065: protected LuaObject(LuaState L, String globalName) {
066: synchronized (L) {
067: this .L = L;
068: L.getGlobal(globalName);
069: registerValue(-1);
070: L.pop(1);
071: }
072: }
073:
074: /**
075: * Creates a reference to an object inside another object
076: *
077: * @param parent
078: * The Lua Table or Userdata that contains the Field.
079: * @param name
080: * The name that index the field
081: */
082: protected LuaObject(LuaObject parent, String name)
083: throws LuaException {
084: synchronized (parent.getLuaState()) {
085: this .L = parent.getLuaState();
086:
087: if (!parent.isTable() && !parent.isUserdata()) {
088: throw new LuaException(
089: "Object parent should be a table or userdata .");
090: }
091:
092: parent.push();
093: L.pushString(name);
094: L.getTable(-2);
095: L.remove(-2);
096: registerValue(-1);
097: L.pop(1);
098: }
099: }
100:
101: /**
102: * This constructor creates a LuaObject from a table that is indexed by a number.
103: *
104: * @param parent
105: * The Lua Table or Userdata that contains the Field.
106: * @param name
107: * The name (number) that index the field
108: * @throws LuaException
109: * When the parent object isn't a Table or Userdata
110: */
111: protected LuaObject(LuaObject parent, Number name)
112: throws LuaException {
113: synchronized (parent.getLuaState()) {
114: this .L = parent.getLuaState();
115: if (!parent.isTable() && !parent.isUserdata())
116: throw new LuaException(
117: "Object parent should be a table or userdata .");
118:
119: parent.push();
120: L.pushNumber(name.doubleValue());
121: L.getTable(-2);
122: L.remove(-2);
123: registerValue(-1);
124: L.pop(1);
125: }
126: }
127:
128: /**
129: * This constructor creates a LuaObject from a table that is indexed by a LuaObject.
130: *
131: * @param parent
132: * The Lua Table or Userdata that contains the Field.
133: * @param name
134: * The name (LuaObject) that index the field
135: * @throws LuaException
136: * When the parent object isn't a Table or Userdata
137: */
138: protected LuaObject(LuaObject parent, LuaObject name)
139: throws LuaException {
140: if (parent.getLuaState() != name.getLuaState())
141: throw new LuaException("LuaStates must be the same!");
142: synchronized (parent.getLuaState()) {
143: if (!parent.isTable() && !parent.isUserdata())
144: throw new LuaException(
145: "Object parent should be a table or userdata .");
146:
147: this .L = parent.getLuaState();
148:
149: parent.push();
150: name.push();
151: L.getTable(-2);
152: L.remove(-2);
153: registerValue(-1);
154: L.pop(1);
155: }
156: }
157:
158: /**
159: * Creates a reference to an object in the given index of the stack
160: *
161: * @param L
162: * @param index
163: * of the object on the lua stack
164: */
165: protected LuaObject(LuaState L, int index) {
166: synchronized (L) {
167: this .L = L;
168:
169: registerValue(index);
170: }
171: }
172:
173: /**
174: * Gets the Object's State
175: */
176: public LuaState getLuaState() {
177: return L;
178: }
179:
180: /**
181: * Creates the reference to the object in the registry table
182: *
183: * @param index
184: * of the object on the lua stack
185: */
186: private void registerValue(int index) {
187: synchronized (L) {
188: L.pushValue(index);
189: int key = L.Lref(LuaState.LUA_REGISTRYINDEX.intValue());
190: ref = new Integer(key);
191: }
192: }
193:
194: protected void finalize() {
195: try {
196: synchronized (L) {
197: if (L.getCPtrPeer() != 0)
198: L.LunRef(LuaState.LUA_REGISTRYINDEX.intValue(), ref
199: .intValue());
200: }
201: } catch (Exception e) {
202: System.err.println("Unable to release object " + ref);
203: }
204: }
205:
206: /**
207: * Pushes the object represented by <code>this<code> into L's stack
208: */
209: public void push() {
210: L
211: .rawGetI(LuaState.LUA_REGISTRYINDEX.intValue(), ref
212: .intValue());
213: }
214:
215: public boolean isNil() {
216: synchronized (L) {
217: push();
218: boolean bool = L.isNil(-1);
219: L.pop(1);
220: return bool;
221: }
222: }
223:
224: public boolean isBoolean() {
225: synchronized (L) {
226: push();
227: boolean bool = L.isBoolean(-1);
228: L.pop(1);
229: return bool;
230: }
231: }
232:
233: public boolean isNumber() {
234: synchronized (L) {
235: push();
236: boolean bool = L.isNumber(-1);
237: L.pop(1);
238: return bool;
239: }
240: }
241:
242: public boolean isString() {
243: synchronized (L) {
244: push();
245: boolean bool = L.isString(-1);
246: L.pop(1);
247: return bool;
248: }
249: }
250:
251: public boolean isFunction() {
252: synchronized (L) {
253: push();
254: boolean bool = L.isFunction(-1);
255: L.pop(1);
256: return bool;
257: }
258: }
259:
260: public boolean isJavaObject() {
261: synchronized (L) {
262: push();
263: boolean bool = L.isObject(-1);
264: L.pop(1);
265: return bool;
266: }
267: }
268:
269: public boolean isJavaFunction() {
270: synchronized (L) {
271: push();
272: boolean bool = L.isJavaFunction(-1);
273: L.pop(1);
274: return bool;
275: }
276: }
277:
278: public boolean isTable() {
279: synchronized (L) {
280: push();
281: boolean bool = L.isTable(-1);
282: L.pop(1);
283: return bool;
284: }
285: }
286:
287: public boolean isUserdata() {
288: synchronized (L) {
289: push();
290: boolean bool = L.isUserdata(-1);
291: L.pop(1);
292: return bool;
293: }
294: }
295:
296: public int type() {
297: synchronized (L) {
298: push();
299: int type = L.type(-1);
300: L.pop(1);
301: return type;
302: }
303: }
304:
305: public boolean getBoolean() {
306: synchronized (L) {
307: push();
308: boolean bool = L.toBoolean(-1);
309: L.pop(1);
310: return bool;
311: }
312: }
313:
314: public double getNumber() {
315: synchronized (L) {
316: push();
317: double db = L.toNumber(-1);
318: L.pop(1);
319: return db;
320: }
321: }
322:
323: public String getString() {
324: synchronized (L) {
325: push();
326: String str = L.toString(-1);
327: L.pop(1);
328: return str;
329: }
330: }
331:
332: public Object getObject() throws LuaException {
333: synchronized (L) {
334: push();
335: Object obj = L.getObjectFromUserdata(-1);
336: L.pop(1);
337: return obj;
338: }
339: }
340:
341: /**
342: * If <code>this<code> is a table or userdata tries to set
343: * a field value.
344: */
345: public LuaObject getField(String field) throws LuaException {
346: return L.getLuaObject(this , field);
347: }
348:
349: /**
350: * Calls the object represented by <code>this</code> using Lua function pcall.
351: *
352: * @param args -
353: * Call arguments
354: * @param nres -
355: * Number of objects returned
356: * @return Object[] - Returned Objects
357: * @throws LuaException
358: */
359: public Object[] call(Object[] args, int nres) throws LuaException {
360: synchronized (L) {
361: if (!isFunction() && !isTable() && !isUserdata())
362: throw new LuaException(
363: "Invalid object. Not a function, table or userdata .");
364:
365: int top = L.getTop();
366: push();
367: int nargs;
368: if (args != null) {
369: nargs = args.length;
370: for (int i = 0; i < nargs; i++) {
371: Object obj = args[i];
372: L.pushObjectValue(obj);
373: }
374: } else
375: nargs = 0;
376:
377: int err = L.pcall(nargs, nres, 0);
378:
379: if (err != 0) {
380: String str;
381: if (L.isString(-1)) {
382: str = L.toString(-1);
383: L.pop(1);
384: } else
385: str = "";
386:
387: if (err == LuaState.LUA_ERRRUN.intValue()) {
388: str = "Runtime error. " + str;
389: } else if (err == LuaState.LUA_ERRMEM.intValue()) {
390: str = "Memory allocation error. " + str;
391: } else if (err == LuaState.LUA_ERRERR.intValue()) {
392: str = "Error while running the error handler function. "
393: + str;
394: } else {
395: str = "Lua Error code " + err + ". " + str;
396: }
397:
398: throw new LuaException(str);
399: }
400:
401: if (nres == LuaState.LUA_MULTRET.intValue())
402: nres = L.getTop() - top;
403: if (L.getTop() - top < nres) {
404: throw new LuaException("Invalid Number of Results .");
405: }
406:
407: Object[] res = new Object[nres];
408:
409: for (int i = nres; i > 0; i--) {
410: res[i - 1] = L.toJavaObject(-1);
411: L.pop(1);
412: }
413: return res;
414: }
415: }
416:
417: /**
418: * Calls the object represented by <code>this</code> using Lua function pcall. Returns 1 object
419: *
420: * @param args -
421: * Call arguments
422: * @return Object - Returned Object
423: * @throws LuaException
424: */
425: public Object call(Object[] args) throws LuaException {
426: return call(args, 1)[0];
427: }
428:
429: public String toString() {
430: synchronized (L) {
431: try {
432: if (isNil())
433: return "nil";
434: else if (isBoolean())
435: return String.valueOf(getBoolean());
436: else if (isNumber())
437: return String.valueOf(getNumber());
438: else if (isString())
439: return getString();
440: else if (isFunction())
441: return "Lua Function";
442: else if (isJavaObject())
443: return getObject().toString();
444: else if (isUserdata())
445: return "Userdata";
446: else if (isTable())
447: return "Lua Table";
448: else if (isJavaFunction())
449: return "Java Function";
450: else
451: return null;
452: } catch (LuaException e) {
453: return null;
454: }
455: }
456: }
457:
458: /**
459: * Function that creates a java proxy to the object represented by <code>this</code>
460: *
461: * @param implem
462: * Interfaces that are implemented, separated by <code>,</code>
463: */
464: public Object createProxy(String implem)
465: throws ClassNotFoundException, LuaException {
466: synchronized (L) {
467: if (!isTable())
468: throw new LuaException("Invalid Object. Must be Table.");
469:
470: StringTokenizer st = new StringTokenizer(implem, ",");
471: Class[] interfaces = new Class[st.countTokens()];
472: for (int i = 0; st.hasMoreTokens(); i++)
473: interfaces[i] = Class.forName(st.nextToken());
474:
475: InvocationHandler handler = new LuaInvocationHandler(this);
476:
477: return Proxy.newProxyInstance(this.getClass()
478: .getClassLoader(), interfaces, handler);
479: }
480: }
481: }
|