001: /*
002: * This is free software, licensed under the Gnu Public License (GPL)
003: * get a copy from <http://www.gnu.org/licenses/gpl.html>
004: * $Id: PluginCommand.java,v 1.8 2005/11/27 16:20:28 hzeller Exp $
005: * author: Henner Zeller <H.Zeller@acm.org>
006: */
007: package henplus.commands;
008:
009: import henplus.AbstractCommand;
010: import henplus.Command;
011: import henplus.CommandDispatcher;
012: import henplus.HenPlus;
013: import henplus.SQLSession;
014: import henplus.io.ConfigurationContainer;
015: import henplus.view.Column;
016: import henplus.view.ColumnMetaData;
017: import henplus.view.TableRenderer;
018: import henplus.view.util.SortedMatchIterator;
019:
020: import java.io.BufferedReader;
021: import java.io.InputStream;
022: import java.io.InputStreamReader;
023: import java.io.OutputStream;
024: import java.io.OutputStreamWriter;
025: import java.io.PrintWriter;
026: import java.util.Iterator;
027: import java.util.Map;
028: import java.util.SortedMap;
029: import java.util.StringTokenizer;
030: import java.util.TreeMap;
031:
032: /**
033: * A Command that handles Plugins.
034: */
035: public final class PluginCommand extends AbstractCommand {
036: private final static String PLUGINS_FILENAME = "plugins";
037: private final static ColumnMetaData[] DRV_META;
038: static {
039: DRV_META = new ColumnMetaData[2];
040: DRV_META[0] = new ColumnMetaData("plugin class");
041: DRV_META[1] = new ColumnMetaData("commands");
042: }
043:
044: private final SortedMap/*<ClassName-String,Command-Class>*/_plugins;
045: private final HenPlus _henplus;
046: private final ConfigurationContainer _config;
047:
048: /**
049: * returns the command-strings this command can handle.
050: */
051: public String[] getCommandList() {
052: return new String[] { "list-plugins", "plug-in", "plug-out" };
053: }
054:
055: public PluginCommand(HenPlus henplus) {
056: _henplus = henplus;
057: _plugins = new TreeMap();
058: _config = henplus
059: .createConfigurationContainer(PLUGINS_FILENAME);
060: }
061:
062: /**
063: * initial load of plugins.
064: */
065: public void load() {
066: _config.read(new ConfigurationContainer.ReadAction() {
067: public void readConfiguration(InputStream inStream)
068: throws Exception {
069: if (inStream == null)
070: return;
071: BufferedReader in = new BufferedReader(
072: new InputStreamReader(inStream, "UTF-8"));
073: String line;
074: while ((line = in.readLine()) != null) {
075: line = line.trim();
076: if (line.length() == 0)
077: continue;
078: Command plugin = null;
079: try {
080: plugin = loadPlugin(line);
081: } catch (Exception e) {
082: HenPlus.msg().println(
083: "couldn't load plugin '" + line + "' "
084: + e.getMessage());
085: }
086: _plugins.put(line, plugin);
087: }
088: in.close();
089: }
090: });
091: }
092:
093: public boolean requiresValidSession(String cmd) {
094: return false;
095: }
096:
097: /**
098: * load a plugin and register it at HenPlus.
099: */
100: private Command loadPlugin(String className)
101: throws ClassNotFoundException, ClassCastException,
102: InstantiationException, IllegalAccessException {
103: Command plugin = null;
104: Class pluginClass = Class.forName(className);
105: plugin = (Command) pluginClass.newInstance();
106: _henplus.getDispatcher().register(plugin);
107: return plugin;
108: }
109:
110: /**
111: * execute the command given.
112: */
113: public int execute(SQLSession currentSession, String cmd,
114: String param) {
115: StringTokenizer st = new StringTokenizer(param);
116: int argc = st.countTokens();
117:
118: if ("list-plugins".equals(cmd)) {
119: if (argc != 0)
120: return SYNTAX_ERROR;
121: HenPlus.msg().println("loaded plugins are marked with '*'");
122: DRV_META[0].resetWidth();
123: DRV_META[1].resetWidth();
124: TableRenderer table = new TableRenderer(DRV_META, HenPlus
125: .out());
126: Iterator it = _plugins.entrySet().iterator();
127: while (it.hasNext()) {
128: Map.Entry entry = (Map.Entry) it.next();
129: Column[] row = new Column[2];
130: Command c = (Command) entry.getValue();
131: String clsName = (String) entry.getKey();
132: row[0] = new Column(((c != null) ? "* " : " ")
133: + clsName);
134: if (c != null) {
135: StringBuffer cmds = new StringBuffer();
136: String[] cmdList = c.getCommandList();
137: for (int i = 0; i < cmdList.length; ++i) {
138: cmds.append(cmdList[i]).append("\n");
139: }
140: row[1] = new Column(cmds.toString().trim());
141: } else {
142: row[1] = new Column(null);
143: }
144: table.addRow(row);
145: }
146: table.closeTable();
147: return SUCCESS;
148: } else if ("plug-in".equals(cmd)) {
149: if (argc != 1)
150: return SYNTAX_ERROR;
151: String pluginClass = (String) st.nextElement();
152: if (_plugins.containsKey(pluginClass)) {
153: HenPlus.msg().println(
154: "plugin '" + pluginClass + "' already loaded");
155: return EXEC_FAILED;
156: }
157: Command plugin = null;
158: try {
159: plugin = loadPlugin(pluginClass);
160: } catch (Exception e) {
161: HenPlus.msg().println(
162: "couldn't load plugin: " + e.getMessage());
163: return EXEC_FAILED;
164: }
165: if (plugin != null) {
166: _plugins.put(pluginClass, plugin);
167: String[] cmds = plugin.getCommandList();
168: HenPlus.out().print("adding commands: ");
169: for (int i = 0; i < cmds.length; ++i) {
170: if (i != 0)
171: HenPlus.out().print(", ");
172: HenPlus.out().print(cmds[i]);
173: }
174: HenPlus.out().println();
175: }
176: } else if ("plug-out".equals(cmd)) {
177: if (argc != 1)
178: return SYNTAX_ERROR;
179: String pluginClass = (String) st.nextElement();
180: if (!_plugins.containsKey(pluginClass)) {
181: HenPlus.msg().println(
182: "unknown plugin '" + pluginClass + "'");
183: return EXEC_FAILED;
184: } else {
185: Command c = (Command) _plugins.remove(pluginClass);
186: _henplus.getDispatcher().unregister(c);
187: }
188: }
189: return SUCCESS;
190: }
191:
192: public Iterator complete(CommandDispatcher disp,
193: String partialCommand, final String lastWord) {
194: StringTokenizer st = new StringTokenizer(partialCommand);
195: String cmd = (String) st.nextElement();
196: int argc = st.countTokens();
197: // list-plugins gets no names.
198: if ("list-plugins".equals(cmd))
199: return null;
200: // do not complete beyond first word.
201: if (argc > ("".equals(lastWord) ? 0 : 1)) {
202: return null;
203: }
204: return new SortedMatchIterator(lastWord, _plugins);
205: }
206:
207: public void shutdown() {
208: _config.write(new ConfigurationContainer.WriteAction() {
209: public void writeConfiguration(OutputStream outStream)
210: throws Exception {
211: PrintWriter out = new PrintWriter(
212: new OutputStreamWriter(outStream, "UTF-8"));
213: Iterator it = _plugins.keySet().iterator();
214: while (it.hasNext()) {
215: out.println((String) it.next());
216: }
217: out.close();
218: }
219: });
220: }
221:
222: /**
223: * return a descriptive string.
224: */
225: public String getShortDescription() {
226: return "handle Plugins";
227: }
228:
229: public String getSynopsis(String cmd) {
230: if ("list-plugins".equals(cmd))
231: return cmd;
232: return cmd + " <plugin-class>";
233: }
234:
235: public String getLongDescription(String cmd) {
236: String dsc = null;
237: if ("plug-in".equals(cmd)) {
238: dsc = "\tLoad plugin. This command takes as argument the name of\n"
239: + "\tthe class that implements the plugin, that is then\n"
240: + "\tloaded from your classpath. The plugin then behaves like\n"
241: + "\tany other built-in command (including help and completion).\n\n"
242: + "\tWriting a plugin is simple: Just write a class that\n"
243: + "\timplements the well documented henplus.Command interface.\n"
244: + "\tYou can just simply derive from henplus.AbstractCommand\n"
245: + "\tthat already implements the default behaviour. An example\n"
246: + "\tof a plugin is the henplus.SamplePlugin that does nothing\n"
247: + "\tbut shows how it works; try it:\n\n"
248: + "\tplug-in henplus.SamplePlugin\n\n"
249: + "\tOn exit of HenPlus, all names of the plugin-classes are\n"
250: + "\tstored, so that they are automatically loaded on next\n"
251: + "\tstartup.";
252: } else if ("plug-out".equals(cmd)) {
253: dsc = "\tUnload plugin. Unload a previously loaded plugin. This\n"
254: + "\tcommand provides completion for the class name.\n";
255: } else if ("list-plugins".equals(cmd)) {
256: dsc = "\tList the plugins loaded. The plugins, that are actually\n"
257: + "\tloaded have a little star (*) in the first column. If it\n"
258: + "\tis not loaded, then you have to extend your CLASSPATH to\n"
259: + "\taccess the plugins class.";
260: }
261: return dsc;
262: }
263: }
264:
265: /*
266: * Local variables:
267: * c-basic-offset: 4
268: * compile-command: "ant -emacs -find build.xml"
269: * End:
270: */
|