001: /*
002: * @(#)ModuleBase.java 1.4 05/01/19
003: *
004: * Copyright (c) 1997-2005 Sun Microsystems, Inc. All Rights Reserved.
005: *
006: * See the file "LICENSE.txt" for information on usage and redistribution
007: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
008: */
009: package pnuts.ext;
010:
011: import java.io.IOException;
012: import java.io.ObjectInputStream;
013: import java.io.Serializable;
014: import java.text.MessageFormat;
015: import java.util.ResourceBundle;
016:
017: import org.pnuts.util.MemoryCache;
018:
019: import pnuts.lang.AutoloadHook;
020: import pnuts.lang.Context;
021: import pnuts.lang.Executable;
022: import pnuts.lang.Package;
023: import pnuts.lang.Pnuts;
024: import pnuts.lang.PnutsException;
025: import pnuts.lang.PnutsFunction;
026: import pnuts.lang.Property;
027: import pnuts.lang.Runtime;
028:
029: /**
030: * Base class of modules. This class provides convenient autloading functions
031: * and an error reporting function. Modules may (or may not) subclass this
032: * class.
033: */
034: public abstract class ModuleBase implements Executable, Serializable {
035:
036: transient private MemoryCache cache = new MemoryCache();
037:
038: AutoloadFunction getAutoloadFunctionHook(Package pkg) {
039: AutoloadFunction af = (AutoloadFunction) cache.get(pkg);
040: if (af == null) {
041: af = new AutoloadFunction(pkg);
042: cache.put(pkg, af);
043: }
044: return af;
045: }
046:
047: /**
048: * Registers an autoloaded script for functionNames. Also, functionNames are
049: * automatically exported.
050: *
051: * @param functionNames
052: * the function names
053: * @param file
054: * a script file to be loaded when one of the functionNames is
055: * first resolved.
056: * @param context
057: * the context
058: */
059: protected void autoload(String[] functionNames, String file,
060: Context context) {
061: Package pkg = context.getCurrentPackage();
062: for (int i = 0; i < functionNames.length; i++) {
063: String symbol = functionNames[i].intern();
064: pkg.autoload(symbol, file, context);
065: pkg.export(symbol);
066: }
067: }
068:
069: /**
070: * Registers an autoloaded class for functionName. Also, the functionName is
071: * automatically exported.
072: *
073: * @param functionName
074: * the function name
075: * @param context
076: * the context
077: */
078: protected void autoloadFunction(String functionName, Context context) {
079: Package pkg = context.getCurrentPackage();
080: String symbol = functionName.intern();
081: pkg.autoload(symbol, getAutoloadFunctionHook(pkg));
082: pkg.export(symbol);
083: }
084:
085: /**
086: * Registers an autoloaded Class object.
087: *
088: * @param javaPackage
089: * Java package name, e.g. "java.util"
090: * @param name
091: * short Class name, e.g. "HashMap"
092: * @param context
093: * the context
094: */
095: protected void autoloadClass(String javaPackage, String name,
096: Context context) {
097: Package pkg = context.getCurrentPackage();
098: String symbol = name.intern();
099: pkg.autoload(symbol, new AutoloadClass(pkg, javaPackage));
100: pkg.export(symbol);
101: }
102:
103: /**
104: * Defines the prefix of script class (resource) name.
105: *
106: * This method is overriden by subclasses.
107: */
108: protected String getPrefix() {
109: return null;
110: }
111:
112: /*
113: * Returns the Package object associated with this module.
114: *
115: * @param context the context @return the package
116: */
117: protected Package getPackage(Context context) {
118: return context.getCurrentPackage();
119: }
120:
121: /**
122: * This method is redefined in subclasses so that package private classes
123: * can be used.
124: *
125: * @param cls
126: * the class to be instantiated
127: * @return an instance of the class
128: */
129: protected Object newInstance(Class cls)
130: throws IllegalAccessException, InstantiationException {
131: return cls.newInstance();
132: }
133:
134: /**
135: * Makes a class name for the specified package and the symbol's name.
136: *
137: * @param pkg
138: * the package
139: * @param name
140: * the symbol
141: * @return the name of the class
142: */
143: protected String getClassName(Package pkg, String name) {
144: String pkgName = pkg.getName();
145: StringBuffer sbuf = new StringBuffer();
146: String prefix = getPrefix();
147: if (prefix != null) {
148: sbuf.append(prefix);
149: sbuf.append('.');
150: }
151: sbuf.append(pkgName);
152: sbuf.append('.');
153: sbuf.append(name);
154: return sbuf.toString().replace('-', '_');
155: }
156:
157: class AutoloadFunction implements AutoloadHook, Serializable {
158: Package pkg;
159:
160: AutoloadFunction(Package pkg) {
161: this .pkg = pkg;
162: }
163:
164: public void load(String name, Context context) {
165: try {
166: String className = getClassName(pkg, name);
167: Class cls = Pnuts.loadClass(className, context);
168: PnutsFunction func = (PnutsFunction) newInstance(cls);
169: pkg.set(name, func, context);
170: } catch (Exception e) {
171: Runtime.printError(e, context);
172: }
173: }
174: }
175:
176: static class AutoloadClass implements AutoloadHook, Serializable {
177: Package pnutsPackage;
178: String javaPackage;
179:
180: AutoloadClass(Package pnutsPackage, String javaPackage) {
181: this .pnutsPackage = pnutsPackage;
182: this .javaPackage = javaPackage;
183: }
184:
185: public void load(String name, Context context) {
186: try {
187: String className = javaPackage + "." + name;
188: Class cls = Pnuts.loadClass(className, context);
189: pnutsPackage.set(name, cls, context);
190: } catch (Exception e) {
191: if (context.isVerbose()) {
192: Runtime.printError(e, context);
193: }
194: }
195: }
196: }
197:
198: private final static String SYMBOL_ERROR = "ERROR".intern();
199:
200: private final static String SYMBOL_EXPORTS = "EXPORTS".intern();
201:
202: /**
203: * Defines ERROR and EXPORTS, and then call execute(Context).
204: *
205: * @param context
206: * the context
207: */
208: public Object run(Context context) {
209: Package pkg = getPackage(context);
210: pkg.set(SYMBOL_ERROR, errorFunction(), context);
211: pkg.set(SYMBOL_EXPORTS, exports(pkg), context);
212: String[] subModules = getSubModules();
213: boolean refreshed = false;
214: if (subModules != null) {
215: for (int i = 0; i < subModules.length; i++) {
216: String module = subModules[i];
217: context.usePackage(module);
218: }
219: context.clearPackages();
220: refreshed = true;
221: }
222: String[] requiredModules = getRequiredModules();
223: if (requiredModules != null) {
224: if (!refreshed) {
225: context.clearPackages();
226: }
227: for (int i = 0; i < requiredModules.length; i++) {
228: String module = requiredModules[i];
229: context.usePackage(module);
230: }
231: }
232: return execute(context);
233: }
234:
235: /**
236: * Subclasses should override this method, instead of run(Context), to
237: * define the initialization process.
238: *
239: * If neither getSubModules() nor getRequiredModules() are redefined
240: * to return non-null value, execute() method should be implemented
241: * as the following steps.
242: * 1. Call context.usePackage() to use the modules that this module provides
243: * 2. Call context.clearPackages()
244: * 3. Call context.usePackage() to use the module that this module requires
245: * 4. Define symbols (functions)
246: *
247: * @param context
248: * the context
249: */
250: protected Object execute(Context context) {
251: return null;
252: }
253:
254: /**
255: * This method is supposed to be redefined in a subclass to
256: * define a set of modules that are required to implement this module.
257: *
258: * If this method returns an array of module names (non-null),
259: * Context.clearPackages() is called, then the modules are used()'d,
260: * before calling execute() method.
261: */
262: protected String[] getRequiredModules() {
263: return null;
264: }
265:
266: /**
267: * This method is supposed to be redefined in a subclass to
268: * define a set of modules that this module provides in the caller's
269: * context.
270: *
271: * If this method returns an array of module names (non-null),
272: * they are use()'d, then Context.clearPackages() is called, before
273: * calling execute() method.
274: */
275: protected String[] getSubModules() {
276: return null;
277: }
278:
279: static class ExportProperty implements Property, Serializable {
280: Package pkg;
281:
282: ExportProperty(Package pkg) {
283: this .pkg = pkg;
284: }
285:
286: public Object get(String name, Context context) {
287: pkg.export(name);
288: return null;
289: }
290:
291: public void set(String name, Object value, Context context) {
292: pkg.set(name, value, context);
293: pkg.export(name);
294: }
295: }
296:
297: static Property exports(final Package pkg) {
298: return new ExportProperty(pkg);
299: }
300:
301: /*
302: * function ERROR(errorID, arg1, ... )
303: */
304: static PnutsFunction errorFunction() {
305: return new PnutsFunction() {
306: public boolean defined(int nargs) {
307: return nargs > 0;
308: }
309:
310: protected Object exec(Object[] args, Context context) {
311: int nargs = args.length;
312: String key = (String) args[0];
313: String[] param = null;
314: if (nargs > 1) {
315: param = new String[nargs - 1];
316: for (int i = 0; i < param.length; i++) {
317: param[i] = (String) args[i + 1];
318: }
319: }
320: Package pkg = context.getCurrentPackage();
321: String rsrc = pkg.getName().replace('.', '/')
322: + "/errors";
323: ResourceBundle bundle = ResourceBundle.getBundle(rsrc);
324: String msg = bundle.getString(key);
325: if (param != null && param.length > 0) {
326: throw new PnutsException(MessageFormat.format(msg,
327: param), context);
328: } else {
329: throw new PnutsException(msg, context);
330: }
331: }
332: };
333: }
334:
335: private void readObject(ObjectInputStream s) throws IOException,
336: ClassNotFoundException {
337: s.defaultReadObject();
338: cache = new MemoryCache();
339: }
340: }
|