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