001: /*
002: * Extension.java --
003: *
004: * Copyright (c) 1997 Cornell University.
005: * Copyright (c) 1997 Sun Microsystems, Inc.
006: *
007: * See the file "license.terms" for information on usage and
008: * redistribution of this file, and for a DISCLAIMER OF ALL
009: * WARRANTIES.
010: *
011: * RCS: @(#) $Id: Extension.java,v 1.5 2006/04/13 07:36:50 mdejong Exp $
012: *
013: */
014:
015: package tcl.lang;
016:
017: /**
018: * Base class for all Tcl Extensions. A Tcl Extension defines a set of
019: * commands that can be loaded into an Interp as a single unit.
020: *
021: * When a Tcl Extension is loaded into an Interp, either statically
022: * (using the "new" operator inside Java code) or dynamically (using
023: * the java::load command in Tcl scripts), it usually creates a set of
024: * commands inside the interpreter. Occasionally, loading an Extension
025: * may lead to additional side effects. For example, a communications
026: * Extension may open network connections when it's loaded. Please
027: * refer to the documentation of the specific Extension for details.
028: */
029:
030: abstract public class Extension {
031:
032: /**
033: * Default constructor. Does nothing. The purpose of this
034: * constructor is to make sure instances of this Extension can be
035: * loaded dynamically using the "java::load" command, which calls
036: * Class.newInstance().
037: */
038:
039: public Extension() {
040: }
041:
042: /**
043: * Initialize the Extension to run in a normal (unsafe)
044: * interpreter. This usually means creating all the commands
045: * provided by this class. A particular implementation can arrange
046: * the commands to be loaded on-demand using the loadOnDemand()
047: * function.
048: *
049: * @param interp current interpreter.
050: */
051:
052: abstract public void init(Interp interp) throws TclException;
053:
054: /**
055: * Initialize the Extension to run in a safe interpreter. This
056: * method should be written carefully, so that it initializes the
057: * safe interpreter only with partial functionality provided by
058: * the Extension that is safe for use by untrusted code.
059: *
060: * The default implementation always throws a TclException, so that
061: * a subclass of Extension cannot be loaded into a safe interpreter
062: * unless it has overridden the safeInit() method.
063: *
064: * @param safeInterp the safe interpreter in which the Extension should
065: * be initialized.
066: */
067:
068: public void safeInit(Interp safeInterp) throws TclException {
069: throw new TclException(safeInterp, "Extension \""
070: + getClass().toString()
071: + "\" cannot be loaded into a safe interpreter");
072: }
073:
074: /**
075: * Create a stub command which autoloads the real command the first time
076: * the stub command is invoked. Register the stub command in the
077: * interpreter.
078: *
079: * @param interp current interp.
080: * @param cmdName name of the command, e.g., "after".
081: * @param clsName name of the Java class that implements this command,
082: * e.g. "tcl.lang.AfterCmd"
083: */
084:
085: public static final void loadOnDemand(Interp interp,
086: String cmdName, String clsName) {
087: interp.createCommand(cmdName, new AutoloadStub(clsName));
088: }
089: }
090:
091: /**
092: * The purpose of AutoloadStub is to load-on-demand the classes that
093: * implement Tcl commands. This reduces Jacl start up time and, when
094: * running Jacl off a web page, reduces download time significantly.
095: */
096:
097: class AutoloadStub implements Command {
098: String className;
099:
100: /**
101: * Create a stub command which autoloads the real command the first time
102: * the stub command is invoked.
103: *
104: * @param clsName name of the Java class that implements this command,
105: * e.g. "tcl.lang.AfterCmd"
106: */
107: AutoloadStub(String clsName) {
108: className = clsName;
109: }
110:
111: /**
112: * Load the class that implements the given command and execute it.
113: *
114: * @param interp the current interpreter.
115: * @param argv command arguments.
116: * @exception TclException if error happens inside the real command proc.
117: */
118: public void cmdProc(Interp interp, TclObject[] objv)
119: throws TclException {
120: Command cmd = load(interp, objv[0].toString());
121: cmd.cmdProc(interp, objv);
122: }
123:
124: /**
125: * Load the class that implements the given command, create
126: * the command in the interpreter, and return. This helper
127: * method is provided so to handle the case where a command
128: * wants to create a stub command without executing it.
129: * The qname argument should be the fully qualified name
130: * of the command.
131: */
132:
133: Command load(Interp interp, String qname) throws TclException {
134: Class cmdClass = null;
135: Command cmd;
136:
137: try {
138: TclClassLoader classLoader = (TclClassLoader) interp
139: .getClassLoader();
140: cmdClass = classLoader.loadClass(className);
141: } catch (ClassNotFoundException e) {
142: throw new TclException(interp,
143: "ClassNotFoundException for class \"" + className
144: + "\"");
145: } catch (PackageNameException e) {
146: throw new TclException(interp,
147: "PackageNameException for class \"" + className
148: + "\"");
149: }
150:
151: try {
152: cmd = (Command) cmdClass.newInstance();
153: } catch (IllegalAccessException e1) {
154: throw new TclException(interp,
155: "IllegalAccessException for class \""
156: + cmdClass.getName() + "\"");
157: } catch (InstantiationException e2) {
158: throw new TclException(interp,
159: "InstantiationException for class \""
160: + cmdClass.getName() + "\"");
161: } catch (ClassCastException e3) {
162: throw new TclException(interp,
163: "ClassCastException for class \""
164: + cmdClass.getName() + "\"");
165: }
166: interp.createCommand(qname, cmd);
167: return cmd;
168: }
169: }
|