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).
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.
27 import java
.io
.InputStream
;
28 import java
.io
.IOException
;
31 * Contains Lua's package 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
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;
48 * Which library function this object represents. This value should
49 * be one of the "enums" defined in the class.
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.
62 /** Constructs instance, filling in the 'which' member. */
63 private PackageLib(int which
)
68 private PackageLib(int which
, LuaTable me
)
75 * Implements all of the functions in the Lua package library. Do not
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
)
93 return loaderPreload(L
);
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
);
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));
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
+ "']");
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);
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
);
208 dooptions(L
, module
, L
.getTop());
212 /** Implements require. */
213 private int require(Lua L
)
215 String name
= L
.checkString(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
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
+ "'");
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
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)));
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
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
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));
275 mt
= L
.createTable(0, 1);
276 L
.setMetatable(L
.value(1), mt
);
278 L
.setField(mt
, "__index", L
.getGlobals());
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);
290 L
.setFenv(L
.value(-1), module
);
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)
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.
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
);
339 catch (IOException e_
)
345 private static String
pushnexttemplate(Lua L
, String path
)
349 while (i
< path
.length() && path
.charAt(i
) == PATHSEP
)
351 if (i
== path
.length())
352 return null; // no more templates
353 int l
= path
.indexOf(PATHSEP
, i
);
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
));
365 L
.error("'package." + pname
+ "' must be a string");
366 L
.pushString(""); // error accumulator
369 path
= pushnexttemplate(L
, path
);
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
+ "'");
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
392 int wild
= s
.indexOf(p
, i
);
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
));
403 private static void setpath(Lua L
,
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
);