Don't use popups, use statusbar
[jpcrr.git] / mnj / lua / PackageLib.java
blob23ea7dcd4634f368351ccdbd97d41e6ee9076dee
1 /* $Header: //info.ravenbrook.com/project/jili/version/1.1/code/mnj/lua/PackageLib.java#1 $
2 * Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
3 * All rights reserved.
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
11 * to the following conditions:
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
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
20 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
21 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 package mnj.lua;
27 import java.io.InputStream;
28 import java.io.IOException;
30 /**
31 * Contains Lua's package library.
32 * The library
33 * can be opened using the {@link #open} method.
35 public final class PackageLib extends LuaJavaCallback
37 // Each function in the library corresponds to an instance of
38 // this class which is associated (the 'which' member) with an integer
39 // which is unique within this class. They are taken from the following
40 // set.
41 private static final int MODULE = 1;
42 private static final int REQUIRE = 2;
43 private static final int SEEALL = 3;
44 private static final int LOADER_PRELOAD = 4;
45 private static final int LOADER_LUA = 5;
47 /**
48 * Which library function this object represents. This value should
49 * be one of the "enums" defined in the class.
51 private int which;
53 /**
54 * Module Environment; a reference to the package table so that
55 * package functions can access it without using the global table.
56 * In PUC-Rio this reference is stored in the function's environment.
57 * Not all instances (Lua Java functions) require this member, but
58 * another subclass would be too wasteful.
60 private LuaTable me;
62 /** Constructs instance, filling in the 'which' member. */
63 private PackageLib(int which)
65 this.which = which;
68 private PackageLib(int which, LuaTable me)
70 this.which = which;
71 this.me = me;
74 /**
75 * Implements all of the functions in the Lua package library. Do not
76 * call directly.
77 * @param L the Lua state in which to execute.
78 * @return number of returned parameters, as per convention.
80 public int luaFunction(Lua L)
82 switch (which)
84 case MODULE:
85 return module(L);
86 case REQUIRE:
87 return require(L);
88 case SEEALL:
89 return seeall(L);
90 case LOADER_LUA:
91 return loaderLua(L);
92 case LOADER_PRELOAD:
93 return loaderPreload(L);
95 return 0;
98 /**
99 * Opens the library into the given Lua state. This registers
100 * the symbols of the library in the global table.
101 * @param L The Lua state into which to open.
103 public static void open(Lua L)
105 LuaTable t = L.register("package");
107 g(L, t, "module", MODULE);
108 g(L, t, "require", REQUIRE);
110 r(L, "seeall", SEEALL);
112 L.setField(t, "loaders", L.newTable());
113 p(L, t, LOADER_PRELOAD);
114 p(L, t, LOADER_LUA);
115 setpath(L, t, "path", PATH_DEFAULT); // set field 'path'
117 // set field 'loaded'
118 L.findTable(L.getRegistry(), Lua.LOADED, 1);
119 L.setField(t, "loaded", L.value(-1));
120 L.pop(1);
121 L.setField(t, "preload", L.newTable());
124 /** Register a function. */
125 private static void r(Lua L, String name, int which)
127 PackageLib f = new PackageLib(which);
128 L.setField(L.getGlobal("package"), name, f);
131 /** Register a function in the global table. */
132 private static void g(Lua L, LuaTable t, String name, int which)
134 PackageLib f = new PackageLib(which, t);
135 L.setGlobal(name, f);
139 /** Register a loader in package.loaders. */
140 private static void p(Lua L, LuaTable t, int which)
142 PackageLib f = new PackageLib(which, t);
143 Object loaders = L.getField(t, "loaders");
144 L.rawSetI(loaders, Lua.objLen(loaders)+1, f);
147 private static final String DIRSEP = "/";
148 private static final char PATHSEP = ';';
149 private static final String PATH_MARK = "?";
150 private static final String PATH_DEFAULT = "?.lua;?/init.lua";
152 private static final Object SENTINEL = new Object();
155 * Implements the preload loader. This is conventionally stored
156 * first in the package.loaders table.
158 private int loaderPreload(Lua L)
160 String name = L.checkString(1);
161 Object preload = L.getField(me, "preload");
162 if (!Lua.isTable(preload))
163 L.error("'package.preload' must be a table");
164 Object loader = L.getField(preload, name);
165 if (Lua.isNil(loader)) // not found?
166 L.pushString("\n\tno field package.preload['" + name + "']");
167 L.push(loader);
168 return 1;
172 * Implements the lua loader. This is conventionally stored second in
173 * the package.loaders table.
175 private int loaderLua(Lua L)
177 String name = L.checkString(1);
178 String filename = findfile(L, name, "path");
179 if (filename == null)
180 return 1; // library not found in this path
181 if (L.loadFile(filename) != 0)
182 loaderror(L, filename);
183 return 1; // library loaded successfully
186 /** Implements module. */
187 private int module(Lua L)
189 String modname = L.checkString(1);
190 Object loaded = L.getField(me, "loaded");
191 Object module = L.getField(loaded, modname);
192 if (!Lua.isTable(module)) // not found?
194 // try global variable (and create one if it does not exist)
195 if (L.findTable(L.getGlobals(), modname, 1) != null)
196 return L.error("name conflict for module '" + modname + "'");
197 module = L.value(-1);
198 L.pop(1);
199 // package.loaded = new table
200 L.setField(loaded, modname, module);
202 // check whether table already has a _NAME field
203 if (Lua.isNil(L.getField(module, "_NAME")))
205 modinit(L, module, modname);
207 setfenv(L, module);
208 dooptions(L, module, L.getTop());
209 return 0;
212 /** Implements require. */
213 private int require(Lua L)
215 String name = L.checkString(1);
216 L.setTop(1);
217 // PUC-Rio's use of lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
218 // (package.loaded is kept in the registry in PUC-Rio) is translated
219 // into this:
220 Object loaded = L.getField(me, "loaded");
221 Object module = L.getField(loaded, name);
222 if (L.toBoolean(module)) // is it there?
224 if (module == SENTINEL) // check loops
225 L.error("loop or previous error loading module '" + name + "'");
226 L.push(module);
227 return 1;
229 // else must load it; iterate over available loaders.
230 Object loaders = L.getField(me, "loaders");
231 if (!Lua.isTable(loaders))
232 L.error("'package.loaders' must be a table");
233 L.pushString(""); // error message accumulator
234 for (int i=1; ; ++i)
236 Object loader = Lua.rawGetI(loaders, i); // get a loader
237 if (Lua.isNil(loader))
238 L.error("module '" + name + "' not found:" +
239 L.toString(L.value(-1)));
240 L.push(loader);
241 L.pushString(name);
242 L.call(1, 1); // call it
243 if (Lua.isFunction(L.value(-1))) // did it find module?
244 break; // module loaded successfully
245 else if (Lua.isString(L.value(-1))) // loader returned error message?
246 L.concat(2); // accumulate it
247 else
248 L.pop(1);
250 L.setField(loaded, name, SENTINEL); // package.loaded[name] = sentinel
251 L.pushString(name); // pass name as argument to module
252 L.call(1, 1); // run loaded module
253 if (!Lua.isNil(L.value(-1))) // non-nil return?
255 // package.loaded[name] = returned value
256 L.setField(loaded, name, L.value(-1));
258 module = L.getField(loaded, name);
259 if (module == SENTINEL) // module did not set a value?
261 module = Lua.valueOfBoolean(true); // use true as result
262 L.setField(loaded, name, module); // package.loaded[name] = true
264 L.push(module);
265 return 1;
268 /** Implements package.seeall. */
269 private static int seeall(Lua L)
271 L.checkType(1, Lua.TTABLE);
272 LuaTable mt = L.getMetatable(L.value(1));
273 if (mt == null)
275 mt = L.createTable(0, 1);
276 L.setMetatable(L.value(1), mt);
278 L.setField(mt, "__index", L.getGlobals());
279 return 0;
283 * Helper for module. <var>module</var> parameter replaces PUC-Rio
284 * use of passing it on the stack.
286 static void setfenv(Lua L, Object module)
288 Debug ar = L.getStack(1);
289 L.getInfo("f", ar);
290 L.setFenv(L.value(-1), module);
291 L.pop(1);
295 * Helper for module. <var>module</var> parameter replaces PUC-Rio
296 * use of passing it on the stack.
298 private static void dooptions(Lua L, Object module, int n)
300 for (int i=2; i<=n; ++i)
302 L.pushValue(i); // get option (a function)
303 L.push(module);
304 L.call(1, 0);
309 * Helper for module. <var>module</var> parameter replaces PUC-Rio
310 * use of passing it on the stack.
312 private static void modinit(Lua L, Object module, String modname)
314 L.setField(module, "_M", module); // module._M = module
315 L.setField(module, "_NAME", modname);
316 int dot = modname.lastIndexOf('.'); // look for last dot in module name
317 // Surprisingly, ++dot works when '.' was found and when it wasn't.
318 ++dot;
319 // set _PACKAGE as package name (full module name minus last part)
320 L.setField(module, "_PACKAGE", modname.substring(0, dot));
323 private static void loaderror(Lua L, String filename)
325 L.error("error loading module '" + L.toString(L.value(1)) +
326 "' from file '" + filename + "':\n\t" +
327 L.toString(L.value(-1)));
330 private static boolean readable(String filename)
332 InputStream f = PackageLib.class.getResourceAsStream(filename);
333 if (f == null)
334 return false;
337 f.close();
339 catch (IOException e_)
342 return true;
345 private static String pushnexttemplate(Lua L, String path)
347 int i = 0;
348 // skip seperators
349 while (i < path.length() && path.charAt(i) == PATHSEP)
350 ++i;
351 if (i == path.length())
352 return null; // no more templates
353 int l = path.indexOf(PATHSEP, i);
354 if (l < 0)
355 l = path.length();
356 L.pushString(path.substring(i, l)); // template
357 return path.substring(l);
360 private String findfile(Lua L, String name, String pname)
362 name = gsub(name, ".", DIRSEP);
363 String path = L.toString(L.getField(me, pname));
364 if (path == null)
365 L.error("'package." + pname + "' must be a string");
366 L.pushString(""); // error accumulator
367 while (true)
369 path = pushnexttemplate(L, path);
370 if (path == null)
371 break;
372 String filename = gsub(L.toString(L.value(-1)), PATH_MARK, name);
373 if (readable(filename)) // does file exist and is readable?
374 return filename; // return that file name
375 L.pop(1); // remove path template
376 L.pushString("\n\tno file '" + filename + "'");
377 L.concat(2);
379 return null; // not found
382 /** Almost equivalent to luaL_gsub. */
383 private static String gsub(String s, String p, String r)
385 StringBuffer b = new StringBuffer();
386 // instead of incrementing the char *s, we use the index i
387 int i = 0;
388 int l = p.length();
390 while (true)
392 int wild = s.indexOf(p, i);
393 if (wild < 0)
394 break;
395 b.append(s.substring(i, wild)); // add prefix
396 b.append(r); // add replacement in place of pattern
397 i = wild + l; // continue after 'p'
399 b.append(s.substring(i));
400 return b.toString();
403 private static void setpath(Lua L,
404 LuaTable t,
405 String fieldname,
406 String def)
408 // :todo: consider implementing a user-specified path via
409 // javax.microedition.midlet.MIDlet.getAppProperty or similar.
410 // Currently we just use a default path defined by Jill.
411 L.setField(t, fieldname, def);