浏览代码

Scripting Plugin.

Change-Id: Ib098163cb07ef9b4cbe5abb66c70c9052e117b24
Reviewed-on: http://gerrit.dmdirc.com/283
Reviewed-by: Gregory Holmes <greboid@dmdirc.com>
Tested-by: Gregory Holmes <greboid@dmdirc.com>
tags/0.6.3
Shane Mc Cormack 14 年前
父节点
当前提交
51174f9de6

+ 98
- 0
src/com/dmdirc/addons/scriptplugin/JavaScriptHelper.java 查看文件

@@ -0,0 +1,98 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Shane Mc Cormack
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.scriptplugin;
24
+import java.util.Hashtable;
25
+
26
+/**
27
+ * Used to allow the rhino javascript to do stuff that it otherwise can't, such
28
+ * as setting global variables, string triming and getting a char.
29
+ *
30
+ * @author Shane 'Dataforce' McCormack
31
+ */
32
+public class JavaScriptHelper {
33
+    /** Used to identify the JavaScriptHelper. */
34
+    private static final String myIDString = "DMDIRC-JSH";
35
+    
36
+    /** Used to allow scripts to know if a specific function they need is available. */
37
+    private final int myVersion = 2;
38
+
39
+    /** Hashtable for storing stufff. */
40
+    private static Hashtable<String,Object> globalSettings = new Hashtable<String,Object>();
41
+    
42
+    /**
43
+     * Method to set Stuff.
44
+     *
45
+     * @param setting Name of setting
46
+     * @param value Value of setting
47
+     */
48
+    public void setGlobal(String setting, Object value) {
49
+        if (setting.equals("")) { return; }
50
+        setting = setting.toLowerCase();
51
+        if (globalSettings.containsKey(setting)) { globalSettings.remove(setting); }
52
+        if (value != null) { globalSettings.put(setting,value); }
53
+    }
54
+    
55
+    /**
56
+     * Method to get Stuff.
57
+     *
58
+     * @param setting Name of setting
59
+     * @return Value of setting
60
+     */
61
+    public Object getGlobal(String setting) {
62
+        if (setting.equals("")) { return ""; }
63
+        setting = setting.toLowerCase();
64
+        if (globalSettings.containsKey(setting)) { return globalSettings.get(setting); }
65
+        return null;
66
+    }
67
+    
68
+    /**
69
+     * Method to trim spaces from strings.
70
+     *
71
+     * @param str String to trim
72
+     * @return String without spaces on the ends
73
+     */
74
+    public String trim(String str) { return str.trim(); }
75
+    
76
+    /**
77
+     * Method to get a java Character object.
78
+     *
79
+     * @param str String to make object from
80
+     * @return Character represending first char in the string
81
+     */
82
+    public Character toChar(String str) { return str.charAt(0); }
83
+    
84
+    /**
85
+     * Get the version of JavaScriptHelper in use.
86
+     *
87
+     * @return JavaScriptHelper version
88
+     */
89
+    public int getVersion() { return myVersion; }
90
+    
91
+    /**
92
+     * Get the ID of this JavaScriptHelper.
93
+     * If you extend or modify this, you should change the myIDString variable.
94
+     *
95
+     * @return JavaScriptHelper ID
96
+     */
97
+    public String getID() { return myIDString; }
98
+}

+ 270
- 0
src/com/dmdirc/addons/scriptplugin/ScriptCommand.java 查看文件

@@ -0,0 +1,270 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.scriptplugin;
24
+
25
+import com.dmdirc.commandparser.CommandArguments;
26
+import com.dmdirc.config.IdentityManager;
27
+import com.dmdirc.commandparser.CommandManager;
28
+import com.dmdirc.commandparser.commands.GlobalCommand;
29
+import com.dmdirc.ui.interfaces.InputWindow;
30
+import com.dmdirc.commandparser.commands.IntelligentCommand;
31
+import com.dmdirc.ui.input.AdditionalTabTargets;
32
+import com.dmdirc.ui.input.TabCompletionType;
33
+
34
+import java.util.LinkedList;
35
+import java.util.List;
36
+import java.util.Map;
37
+import java.io.File;
38
+import java.io.FileWriter;
39
+import java.io.IOException;
40
+
41
+import java.lang.reflect.Method;
42
+
43
+/**
44
+ * The Script Command allows controlling of the scriptplugin.
45
+ *
46
+ * @author Shane 'Dataforce' McCormack
47
+ */
48
+public final class ScriptCommand extends GlobalCommand implements IntelligentCommand {
49
+    /** My Plugin */
50
+    final ScriptPlugin myPlugin;
51
+
52
+    /**
53
+     * Creates a new instance of ScriptCommand.
54
+     */
55
+    public ScriptCommand(final ScriptPlugin plugin) {
56
+        super();
57
+        myPlugin = plugin;
58
+        CommandManager.registerCommand(this);
59
+    }
60
+        
61
+    /**
62
+     * Executes this command.
63
+     *
64
+     * @param origin The frame in which this command was issued
65
+     * @param server The server object that this command is associated with
66
+     * @param isSilent Whether this command is silenced or not
67
+     * @param args The user supplied arguments
68
+     */
69
+    @Override
70
+    public void execute(final InputWindow origin, final boolean isSilent, final CommandArguments commandArgs) {
71
+        final String[] args = commandArgs.getArguments();
72
+    
73
+        if (args.length > 0 && (args[0].equalsIgnoreCase("rehash") || args[0].equalsIgnoreCase("reload"))) {
74
+            sendLine(origin, isSilent, FORMAT_OUTPUT, "Reloading scripts");
75
+            myPlugin.rehash();
76
+        } else if (args.length > 0 && args[0].equalsIgnoreCase("load")) {
77
+            if (args.length > 1) {
78
+                final String filename = commandArgs.getArgumentsAsString(1);
79
+                sendLine(origin, isSilent, FORMAT_OUTPUT, "Loading: "+filename+" ["+myPlugin.loadScript(myPlugin.getScriptDir()+filename)+"]");
80
+            } else {
81
+                sendLine(origin, isSilent, FORMAT_ERROR, "You must specify a script to load");
82
+            }
83
+        } else if (args.length > 0 && args[0].equalsIgnoreCase("unload")) {
84
+            if (args.length > 1) {
85
+                final String filename = commandArgs.getArgumentsAsString(1);
86
+                sendLine(origin, isSilent, FORMAT_OUTPUT, "Unloading: "+filename+" ["+myPlugin.loadScript(myPlugin.getScriptDir()+filename)+"]");
87
+            } else {
88
+                sendLine(origin, isSilent, FORMAT_ERROR, "You must specify a script to unload");
89
+            }
90
+        } else if (args.length > 0 && args[0].equalsIgnoreCase("eval")) {
91
+            if (args.length > 1) {
92
+                final String script = commandArgs.getArgumentsAsString(1);
93
+                sendLine(origin, isSilent, FORMAT_OUTPUT, "Evaluating: "+script);
94
+                try {
95
+                    ScriptEngineWrapper wrapper;
96
+                    if (IdentityManager.getGlobalConfig().hasOptionString(myPlugin.getDomain(), "eval.baseFile")) {
97
+                        final String baseFile = myPlugin.getScriptDir()+'/'+IdentityManager.getGlobalConfig().getOption(myPlugin.getDomain(), "eval.baseFile");
98
+                        if ((new File(baseFile)).exists()) {
99
+                            wrapper = new ScriptEngineWrapper(myPlugin, baseFile);
100
+                        } else {
101
+                            wrapper = new ScriptEngineWrapper(myPlugin, null);
102
+                        }
103
+                    } else {
104
+                        wrapper = new ScriptEngineWrapper(myPlugin, null);
105
+                    }
106
+                    wrapper.getScriptEngine().put("cmd_origin", origin);
107
+                    wrapper.getScriptEngine().put("cmd_isSilent", isSilent);
108
+                    wrapper.getScriptEngine().put("cmd_args", args);
109
+                    sendLine(origin, isSilent, FORMAT_OUTPUT, "Result: "+wrapper.getScriptEngine().eval(script));
110
+                } catch (Exception e) {
111
+                    sendLine(origin, isSilent, FORMAT_OUTPUT, "Exception: "+e+" -> "+e.getMessage());
112
+                    
113
+                    if (IdentityManager.getGlobalConfig().getOptionBool(myPlugin.getDomain(), "eval.showStackTrace")) {
114
+                        try {
115
+                            final Class<?> logger = Class.forName("com.dmdirc.logger.Logger");
116
+                            if (logger != null) {
117
+                                final Method exceptionToStringArray = logger.getDeclaredMethod("exceptionToStringArray", new Class[]{Throwable.class});
118
+                                exceptionToStringArray.setAccessible(true);
119
+                                
120
+                                final String[] stacktrace = (String[])exceptionToStringArray.invoke(null, e);
121
+                                for (String line : stacktrace) {
122
+                                    sendLine(origin, isSilent, FORMAT_OUTPUT, "Stack trace: "+line);
123
+                                }
124
+                            }
125
+                        } catch (Exception ex) {
126
+                            sendLine(origin, isSilent, FORMAT_OUTPUT, "Stack trace: Exception showing stack trace: "+ex+" -> "+ex.getMessage());
127
+                        }
128
+                    }
129
+                    
130
+                }
131
+            } else {
132
+                sendLine(origin, isSilent, FORMAT_ERROR, "You must specify some script to eval.");
133
+            }
134
+        } else if (args.length > 0 && args[0].equalsIgnoreCase("savetobasefile")) {
135
+            if (args.length > 2) {
136
+                final String[] bits = args[1].split("/");
137
+                final String functionName = bits[0];
138
+                final String script = commandArgs.getArgumentsAsString(2);
139
+                sendLine(origin, isSilent, FORMAT_OUTPUT, "Saving as '"+functionName+"': "+script);
140
+                if (IdentityManager.getGlobalConfig().hasOptionString(myPlugin.getDomain(), "eval.baseFile")) {
141
+                    try {
142
+                        final String baseFile = myPlugin.getScriptDir()+'/'+IdentityManager.getGlobalConfig().getOption(myPlugin.getDomain(), "eval.baseFile");
143
+                        final FileWriter writer = new FileWriter(baseFile, true);
144
+                        writer.write("function ");
145
+                        writer.write(functionName);
146
+                        writer.write("(");
147
+                        for (int i = 1; i < bits.length; i++) {
148
+                            writer.write(bits[i]);
149
+                            writer.write(" ");
150
+                        }
151
+                        writer.write(") {\n");
152
+                        writer.write(script);
153
+                        writer.write("\n}\n");
154
+                        writer.flush();
155
+                        writer.close();
156
+                    } catch (IOException ioe) {
157
+                        sendLine(origin, isSilent, FORMAT_ERROR, "IOException: "+ioe.getMessage());
158
+                    }
159
+                } else {
160
+                    sendLine(origin, isSilent, FORMAT_ERROR, "No baseFile specified, please /set "+myPlugin.getDomain()+" eval.baseFile filename (stored in scripts dir of profile)");
161
+                }
162
+            } else if (args.length > 1) {
163
+                sendLine(origin, isSilent, FORMAT_ERROR, "You must specify some script to save.");
164
+            } else {
165
+                sendLine(origin, isSilent, FORMAT_ERROR, "You must specify a function name and some script to save.");
166
+            }
167
+        } else if (args.length > 0 && args[0].equalsIgnoreCase("help")) {
168
+            sendLine(origin, isSilent, FORMAT_OUTPUT, "This command allows you to interact with the script plugin");
169
+            sendLine(origin, isSilent, FORMAT_OUTPUT, "-------------------");
170
+            sendLine(origin, isSilent, FORMAT_OUTPUT, "reload/rehash                  - Reload all loaded scripts");
171
+            sendLine(origin, isSilent, FORMAT_OUTPUT, "load <script>                  - load scripts/<script> (file name relative to scripts dir)");
172
+            sendLine(origin, isSilent, FORMAT_OUTPUT, "unload <script>                - unload <script> (full file name)");
173
+            sendLine(origin, isSilent, FORMAT_OUTPUT, "eval <script>                  - evaluate the code <script> and return the result");
174
+            sendLine(origin, isSilent, FORMAT_OUTPUT, "savetobasefile <name> <script> - save the code <script> to the eval basefile ("+myPlugin.getDomain()+".eval.basefile)");
175
+            sendLine(origin, isSilent, FORMAT_OUTPUT, "                                 as the function <name> (name/foo/bar will save it as 'name' with foo and");
176
+            sendLine(origin, isSilent, FORMAT_OUTPUT, "                                 bar as arguments.");
177
+            sendLine(origin, isSilent, FORMAT_OUTPUT, "-------------------");
178
+        } else {
179
+            sendLine(origin, isSilent, FORMAT_ERROR, "Unknown subcommand.");
180
+        }
181
+    }
182
+
183
+    /**
184
+     * Returns a list of suggestions for the specified argument, given the list
185
+     * of previous arguments.
186
+     * @param arg The argument that is being completed
187
+     * @param previousArgs The contents of the previous arguments, if any
188
+     * @return A list of suggestions for the argument
189
+     */
190
+    @Override
191
+    public AdditionalTabTargets getSuggestions(final int arg, final List<String> previousArgs) {
192
+        final AdditionalTabTargets res = new AdditionalTabTargets();
193
+        
194
+        res.excludeAll();
195
+        if (arg == 0) {
196
+            res.add("help");
197
+            res.add("rehash");
198
+            res.add("reload");
199
+            res.add("load");
200
+            res.add("unload");
201
+            res.add("eval");
202
+            res.add("savetobasefile");
203
+        } else if (arg == 1) {
204
+            final Map<String,ScriptEngineWrapper> scripts = myPlugin.getScripts();
205
+            if (previousArgs.get(0).equalsIgnoreCase("load")) {
206
+                for (String filename : getPossibleScripts()) {
207
+                    res.add(filename);
208
+                }
209
+            } else if (previousArgs.get(0).equalsIgnoreCase("unload")) {
210
+                for (String filename : scripts.keySet()) {
211
+                    res.add(filename);
212
+                }
213
+            }
214
+        }
215
+        
216
+        return res;
217
+    }
218
+    
219
+    /**
220
+     * Retrieves a list of all installed scripts.
221
+     * Any file under the main plugin directory (~/.DMDirc/scripts or similar)
222
+     * that matches *.js is deemed to be a valid script.
223
+     *
224
+     * @return A list of all installed scripts
225
+     */
226
+    private List<String> getPossibleScripts() {
227
+        final List<String> res = new LinkedList<String>();
228
+        
229
+        final LinkedList<File> dirs = new LinkedList<File>();
230
+        dirs.add(new File(myPlugin.getScriptDir()));
231
+        
232
+        while (!dirs.isEmpty()) {
233
+            final File dir = dirs.pop();
234
+            if (dir.isDirectory()) {
235
+                for (File file : dir.listFiles()) {
236
+                    dirs.add(file);
237
+                }
238
+            } else if (dir.isFile() && dir.getName().endsWith(".js")) {
239
+                final String target = dir.getPath();
240
+                res.add(target.substring(myPlugin.getScriptDir().length(), target.length()));
241
+            }
242
+        }
243
+        return res;
244
+    }
245
+
246
+    /**
247
+     * Returns this command's name.
248
+     *
249
+     * @return The name of this command
250
+     */
251
+    @Override
252
+    public String getName() { return "script"; }
253
+    
254
+    /**
255
+     * Returns whether or not this command should be shown in help messages.
256
+     *
257
+     * @return True iff the command should be shown, false otherwise
258
+     */
259
+    @Override
260
+    public boolean showInHelp() { return true; }
261
+    
262
+    /**
263
+     * Returns a string representing the help message for this command.
264
+     *
265
+     * @return the help message for this command
266
+     */
267
+    @Override
268
+    public String getHelp() { return "script - Allows controlling the script plugin"; }
269
+}
270
+

+ 145
- 0
src/com/dmdirc/addons/scriptplugin/ScriptEngineWrapper.java 查看文件

@@ -0,0 +1,145 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.scriptplugin;
24
+
25
+import com.dmdirc.logger.Logger;
26
+import com.dmdirc.logger.ErrorLevel;
27
+
28
+import java.io.File;
29
+import java.io.FileNotFoundException;
30
+import java.io.FileReader;
31
+import javax.script.ScriptEngine;
32
+import javax.script.Invocable;
33
+import javax.script.ScriptException;
34
+
35
+/**
36
+ * Class to create script engines!
37
+ *
38
+ * @author Shane 'Dataforce' McCormack
39
+ */
40
+public class ScriptEngineWrapper {
41
+    /** The Script Engine this wrapper wraps */
42
+    private ScriptEngine engine;
43
+
44
+    /** The File this script is from */
45
+    private final File file;
46
+    
47
+    /** Script-Local JS Helper */
48
+    private JavaScriptHelper localHelper = new JavaScriptHelper();
49
+
50
+    /** The script plugin that owns this wrapper. */
51
+    private final ScriptPlugin plugin;
52
+
53
+    /**
54
+     * Create a new ScriptEngineWrapper
55
+     *
56
+     * @param plugin The script plugin that owns this wrapper
57
+     * @param filename Filename of script
58
+     */
59
+    protected ScriptEngineWrapper(final ScriptPlugin plugin, final String filename) throws FileNotFoundException, ScriptException {
60
+        this.plugin = plugin;
61
+        file = (filename != null) ? new File(filename) : null;
62
+        
63
+        engine = createEngine();
64
+        
65
+        callFunction("onLoad");
66
+    }
67
+    
68
+    /**
69
+     * Get a reference to the ScriptEngine.
70
+     *
71
+     * @return a reference to the ScriptEngine
72
+     */
73
+    protected ScriptEngine getScriptEngine() { return engine; }
74
+    
75
+    /**
76
+     * Get a reference to the JavaScriptHelper
77
+     *
78
+     * @return a reference to the JavaScriptHelper
79
+     */
80
+    protected JavaScriptHelper getJavaScriptHelper() { return localHelper; }
81
+    
82
+    /**
83
+     * Get the file for this script
84
+     *
85
+     * @return The file for this script
86
+     */
87
+    protected File getFile() { return file; }
88
+    
89
+    /**
90
+     * Create a new engine for this script
91
+     */
92
+    protected ScriptEngine createEngine() throws FileNotFoundException, ScriptException {
93
+        final ScriptEngine result = plugin.getScriptFactory().getEngineByName("JavaScript");
94
+        if (file != null) {
95
+            result.eval(new FileReader(file));
96
+        }
97
+        
98
+        result.put("localHelper", localHelper);
99
+        result.put("thisEngine", this);
100
+        
101
+        return result;
102
+    }
103
+    
104
+    /**
105
+     * Call a function in this script.
106
+     *
107
+     * @param functionName Name of function
108
+     * @param args Arguments for function
109
+     */
110
+    protected void callFunction(final String functionName, final Object... args) {
111
+        try {
112
+            // Call Function
113
+            final Invocable invEngine = (Invocable) engine;
114
+            invEngine.invokeFunction(functionName, args);
115
+        } catch (NoSuchMethodException nsme) {
116
+            // There is no "methodExists" function, so we catch NoSuchMethodException
117
+            // and do nothing rather that add an erropr every time a method is called
118
+            // that doesn't exist (such as the action_* methods)
119
+        } catch (Exception e) {
120
+            Logger.userError(ErrorLevel.LOW, "Error calling '"+functionName+"' in '"+file.getPath()+"': "+e.getMessage(), e);
121
+        }
122
+    }
123
+    
124
+    /**
125
+     * Try to reload this script.
126
+     */
127
+    protected boolean reload() {
128
+        // Tell the current engine that its about to be obliterated.
129
+        callFunction("onPreRehash");
130
+        
131
+        try {
132
+            // Try making a new engine
133
+            engine = createEngine();
134
+            // Tell it that it has been rehashed
135
+            callFunction("onRehashSucess");
136
+        } catch (Exception e) {
137
+            Logger.userError(ErrorLevel.LOW, "Reloading '"+file.getPath()+"' failed: "+e.getMessage(), e);
138
+            // Tell it that its rehash failed
139
+            callFunction("onRehashFailed", e);
140
+            return false;
141
+        }
142
+        
143
+        return true;
144
+    }
145
+}

+ 270
- 0
src/com/dmdirc/addons/scriptplugin/ScriptPlugin.java 查看文件

@@ -0,0 +1,270 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.scriptplugin;
24
+
25
+import com.dmdirc.Main;
26
+import com.dmdirc.config.prefs.validator.ValidationResponse;
27
+import com.dmdirc.plugins.Plugin;
28
+import com.dmdirc.commandparser.CommandManager;
29
+import com.dmdirc.interfaces.ActionListener;
30
+import com.dmdirc.actions.ActionManager;
31
+import com.dmdirc.actions.CoreActionType;
32
+import com.dmdirc.actions.interfaces.ActionType;
33
+import com.dmdirc.logger.Logger;
34
+import com.dmdirc.logger.ErrorLevel;
35
+
36
+import java.io.File;
37
+import java.io.IOException;
38
+import java.io.FileInputStream;
39
+import java.io.FileOutputStream;
40
+import java.util.ArrayList;
41
+import java.util.List;
42
+import java.util.Map;
43
+import java.util.HashMap;
44
+
45
+import javax.script.ScriptEngineManager;
46
+
47
+/**
48
+ * This allows javascript scripts to be used in DMDirc.
49
+ *
50
+ * @author Shane 'Dataforce' McCormack
51
+ */
52
+public final class ScriptPlugin extends Plugin implements ActionListener {
53
+    /** The ScriptCommand we created */
54
+    private ScriptCommand command = null;
55
+    
56
+    /** Script Directory */
57
+    private final String scriptDir = Main.getConfigDir() + "scripts/";
58
+    
59
+    /** Script Engine Manager */
60
+    private ScriptEngineManager scriptFactory = new ScriptEngineManager();
61
+    
62
+    /** Instance of the javaScriptHelper class */
63
+    private JavaScriptHelper jsHelper = new JavaScriptHelper();
64
+    
65
+    /** Store Script State Name,Engine */
66
+    private Map<String,ScriptEngineWrapper> scripts = new HashMap<String,ScriptEngineWrapper>();
67
+    
68
+    /** Used to store permanent variables */
69
+    protected TypedProperties globalVariables = new TypedProperties();
70
+
71
+    /**
72
+     * Creates a new instance of the Script Plugin.
73
+     */
74
+    public ScriptPlugin() {
75
+        super();
76
+        
77
+        // Add the JS Helper to the scriptFactory
78
+        getScriptFactory().put("globalHelper", getJavaScriptHelper());
79
+        getScriptFactory().put("globalVariables", getGlobalVariables());
80
+    }
81
+    
82
+    /**
83
+     * Called when the plugin is loaded.
84
+     */
85
+    @Override
86
+    public void onLoad() {
87
+        // Register the plugin_loaded action initially, this will be called
88
+        // after this method finishes for us to register the rest.
89
+        ActionManager.addListener(this, CoreActionType.PLUGIN_LOADED);
90
+        command = new ScriptCommand(this);
91
+        
92
+        // Make sure our scripts dir exists
93
+        final File newDir = new File(scriptDir);
94
+        if (!newDir.exists()) { newDir.mkdirs(); }
95
+        
96
+        final File savedVariables = new File(scriptDir+"storedVariables");
97
+        if (savedVariables.exists()) {
98
+            try {
99
+                globalVariables.load(new FileInputStream(savedVariables));
100
+            } catch (IOException e) {
101
+                Logger.userError(ErrorLevel.LOW, "Error reading savedVariables from '"+savedVariables.getPath()+"': "+e.getMessage(), e);
102
+            }
103
+        }
104
+    }
105
+    
106
+    /**
107
+     * Called when this plugin is Unloaded
108
+     */
109
+    @Override
110
+    public void onUnload() {
111
+        ActionManager.removeListener(this);
112
+        CommandManager.unregisterCommand(command);
113
+        
114
+        final File savedVariables = new File(scriptDir+"storedVariables");
115
+        try {
116
+            globalVariables.store(new FileOutputStream(savedVariables), "# DMDirc Script Plugin savedVariables");
117
+        } catch (IOException e) {
118
+            Logger.userError(ErrorLevel.LOW, "Error reading savedVariables to '"+savedVariables.getPath()+"': "+e.getMessage(), e);
119
+        }
120
+    }
121
+    
122
+    /**
123
+     * Register all the action types.
124
+     * This will unregister all the actions first.
125
+     */
126
+    private void registerAll() {
127
+        ActionManager.removeListener(this);
128
+        for (Map.Entry<String, List<ActionType>> entry : ActionManager.getTypeGroups().entrySet()) {
129
+            final List<ActionType> types = entry.getValue();
130
+            ActionManager.addListener(this, types.toArray(new ActionType[0]));
131
+        }
132
+    }
133
+    
134
+    /**
135
+     * Process an event of the specified type.
136
+     *
137
+     * @param type The type of the event to process
138
+     * @param format Format of messages that are about to be sent. (May be null)
139
+     * @param arguments The arguments for the event
140
+     */
141
+    @Override
142
+    public void processEvent(final ActionType type, final StringBuffer format, final Object... arguments) {
143
+        // Plugins may to register/unregister action types, so lets reregister all
144
+        // the action types. This 
145
+        if (type.equals(CoreActionType.PLUGIN_LOADED) || type.equals(CoreActionType.PLUGIN_UNLOADED)) {
146
+            registerAll();
147
+        }
148
+        callFunctionAll("action_"+type.toString().toLowerCase(), arguments);
149
+    }
150
+    
151
+    /**
152
+     * Get a clone of the scripts map.
153
+     *
154
+     * @return a clone of the scripts map
155
+     */
156
+    protected Map<String,ScriptEngineWrapper> getScripts() { return new HashMap<String,ScriptEngineWrapper>(scripts); }
157
+    
158
+    /**
159
+     * Get a reference to the scriptFactory.
160
+     *
161
+     * @return a reference to the scriptFactory
162
+     */
163
+    protected ScriptEngineManager getScriptFactory() { return scriptFactory; }
164
+    
165
+    /**
166
+     * Get a reference to the JavaScriptHelper
167
+     *
168
+     * @return a reference to the JavaScriptHelper
169
+     */
170
+    protected JavaScriptHelper getJavaScriptHelper() { return jsHelper; }
171
+    
172
+    /**
173
+     * Get a reference to the GlobalVariables Properties
174
+     *
175
+     * @return a reference to the GlobalVariables Properties
176
+     */
177
+    protected TypedProperties getGlobalVariables() { return globalVariables; }
178
+    
179
+    /**
180
+     * Get the name of the directory where scripts should be stored.
181
+     *
182
+     * @return The name of the directory where scripts should be stored.
183
+     */
184
+    protected String getScriptDir() { return scriptDir; }
185
+    
186
+    /** Reload all scripts */
187
+    public void rehash() {
188
+        for (final ScriptEngineWrapper engine : scripts.values()) {
189
+            engine.reload();
190
+        }
191
+        // Advise the Garbage collector that now would be a good time to run
192
+        System.gc();
193
+    }
194
+    
195
+    /**
196
+     * Call a function in all scripts.
197
+     *
198
+     * @param functionName Name of function
199
+     * @param args Arguments for function
200
+     */
201
+    private void callFunctionAll(final String functionName, final Object... args) {
202
+        for (final ScriptEngineWrapper engine : scripts.values()) {
203
+            engine.callFunction(functionName, args);
204
+        }
205
+    }
206
+
207
+    /**
208
+     * Unload a script file.
209
+     *
210
+     * @param scriptFilename Path to script
211
+     */
212
+    public void unloadScript(final String scriptFilename) {
213
+        if (scripts.containsKey(scriptFilename)) {
214
+            // Tell it that its about to be unloaded.
215
+            (scripts.get(scriptFilename)).callFunction("onUnload");
216
+            // Remove the script
217
+            scripts.remove(scriptFilename);
218
+            // Advise the Garbage collector that now would be a good time to run
219
+            System.gc();
220
+        }
221
+    }
222
+        
223
+    /**
224
+     * Load a script file into a new jsEngine
225
+     *
226
+     * @param scriptFilename Path to script
227
+     * @return true for Success (or already loaded), false for fail. (Fail occurs if script already exists, or if it has errors)
228
+     */
229
+    public boolean loadScript(final String scriptFilename) {
230
+        if (!scripts.containsKey(scriptFilename)) {
231
+            try {
232
+                final ScriptEngineWrapper wrapper = new ScriptEngineWrapper(this, scriptFilename);
233
+                scripts.put(scriptFilename, wrapper);
234
+            } catch (Exception e) {
235
+                Logger.userError(ErrorLevel.LOW, "Error loading '"+scriptFilename+"': "+e.getMessage(), e);
236
+                return false;
237
+            }
238
+        }
239
+        return true;
240
+    }
241
+    
242
+    /**
243
+     * Check any further Prerequisites for this plugin to load that can not be
244
+     * checked using metainfo.
245
+     *
246
+     * @return ValidationResponse detailign if the plugin passes any extra checks
247
+     *         that plugin.info can't handle
248
+     */
249
+    public ValidationResponse checkPrerequisites() {
250
+        if (getScriptFactory().getEngineByName("JavaScript") == null) {
251
+            return new ValidationResponse("JavaScript Scripting Engine not found.");
252
+        } else {
253
+            return new ValidationResponse();
254
+        }
255
+    }
256
+    
257
+    /**
258
+     * Get the reason for checkPrerequisites failing.
259
+     *
260
+     * @return Human-Readble reason for checkPrerequisites failing.
261
+     */
262
+    public String checkPrerequisitesReason() {
263
+        if (getScriptFactory().getEngineByName("JavaScript") == null) {
264
+            return "JavaScript Scripting Engine not found.";
265
+        } else {
266
+            return "";
267
+        }
268
+    }
269
+}
270
+

+ 421
- 0
src/com/dmdirc/addons/scriptplugin/TypedProperties.java 查看文件

@@ -0,0 +1,421 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Shane Mc Cormack
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+package com.dmdirc.addons.scriptplugin;
23
+
24
+import java.util.ArrayList;
25
+import java.util.List;
26
+import java.util.Properties;
27
+import java.io.InputStream;
28
+import java.io.Reader;
29
+import java.io.IOException;
30
+
31
+/**
32
+ * Properties file that allows for getting/setting of typed properties
33
+ *
34
+ * @author Shane 'Dataforce' McCormack
35
+ */
36
+public class TypedProperties extends Properties {
37
+	/**
38
+	 * A version number for this class.
39
+	 * It should be changed whenever the class structure is changed (or anything
40
+	 * else that would prevent serialized objects being unserialized with the new 
41
+	 * class).
42
+	 */
43
+	private static final long serialVersionUID = 200711071;
44
+	
45
+	/** Is this properties file Case Sensitive */
46
+	private boolean caseSensitive = true;
47
+	
48
+	/**
49
+	 * Creates an empty property list with no default values.
50
+	 */
51
+	public TypedProperties() {
52
+		super();
53
+	}
54
+	
55
+	/**
56
+	 * Creates an empty property list with the specified defaults.
57
+	 *
58
+	 * @param defaults The Defaults
59
+	 */
60
+	public TypedProperties(final Properties defaults) {
61
+		super(defaults);
62
+	}
63
+	
64
+	/**
65
+	 * Set case sensitivity of this properties file.
66
+	 *
67
+	 * @param value True/False for the case sensitivity of this file
68
+	 */
69
+	public void setCaseSensitivity(final boolean value) {
70
+		// Set all existing values to lowercase.
71
+		if (!value) {
72
+			for (Object property : this.keySet()) {
73
+				if (property instanceof String) {
74
+					final String propertyName = (String)property;
75
+					if (!propertyName.equals(propertyName.toLowerCase())) {
76
+						super.setProperty(propertyName.toLowerCase(), getProperty(propertyName));
77
+						super.remove(propertyName);
78
+					}
79
+				}
80
+			}
81
+		}
82
+		caseSensitive = value;
83
+	}
84
+	
85
+	/**
86
+	 * Load properties from an InputStream.
87
+	 * After loading, setCaseSensitivity(caseSensitive) is called.
88
+	 * If this properties file is ment to be case Insensitive, all non-lowercase
89
+	 * property names will be lowercased.
90
+	 *
91
+	 * @param inStream InputStream to load from.
92
+	 * @throws IOException If there is a problem reading from the Input Stream
93
+	 */
94
+	@Override
95
+	public void load(final InputStream inStream) throws IOException {
96
+		super.load(inStream);
97
+		setCaseSensitivity(caseSensitive);
98
+	}
99
+	
100
+	/**
101
+	 * Load properties from a Reader.
102
+	 * After loading, setCaseSensitivity(caseSensitive) is called.
103
+	 * If this properties file is ment to be case Insensitive, all non-lowercase
104
+	 * property names will be lowercased.
105
+	 *
106
+	 * @param reader Reader to load from.
107
+	 * @throws IOException If there is an error reading from the reader
108
+	 */
109
+	@Override
110
+	public void load(final Reader reader) throws IOException {
111
+		super.load(reader);
112
+		setCaseSensitivity(caseSensitive);
113
+	}
114
+	
115
+	/**
116
+	 * Load properties from an XML InputStream.
117
+	 * After loading, setCaseSensitivity(caseSensitive) is called.
118
+	 * If this properties file is ment to be case Insensitive, all non-lowercase
119
+	 * property names will be lowercased.
120
+	 *
121
+	 * @param in InputStream to load from.
122
+	 * @throws java.io.IOException 
123
+	 */
124
+	@Override
125
+	public void loadFromXML(final InputStream in) throws IOException {
126
+		super.loadFromXML(in);
127
+		setCaseSensitivity(caseSensitive);
128
+	}
129
+	
130
+	/**
131
+	 * Get a property from the config
132
+	 *
133
+	 * @param key key for property
134
+	 * @return the requested property, or null if not defined
135
+	 */
136
+	@Override
137
+	public String getProperty(final String key) {
138
+		if (!caseSensitive) {
139
+			return super.getProperty(key.toLowerCase());
140
+		} else {
141
+			return super.getProperty(key);
142
+		}
143
+	}
144
+	
145
+	/**
146
+	 * Get a property from the config
147
+	 *
148
+	 * @param key key for property
149
+	 * @param fallback Value to return if key is not found
150
+	 * @return the requested property, or the fallback value if not defined
151
+	 */
152
+	@Override
153
+	public String getProperty(final String key, final String fallback) {
154
+		if (!caseSensitive) {
155
+			return super.getProperty(key.toLowerCase(), fallback);
156
+		} else {
157
+			return super.getProperty(key, fallback);
158
+		}
159
+	}
160
+	
161
+	/**
162
+	 * Set a property in the config
163
+	 *
164
+	 * @param key key for property
165
+	 * @param value Value for property
166
+	 * @return Old value of property
167
+	 */
168
+	@Override
169
+	public Object setProperty(final String key, final String value) {
170
+		if (!caseSensitive) {
171
+			return super.setProperty(key.toLowerCase(), value);
172
+		} else {
173
+			return super.setProperty(key, value);
174
+		}
175
+	}
176
+	
177
+	/**
178
+	 * Check if a property exists
179
+	 *
180
+	 * @param key key for property
181
+	 * @return True if the property exists, else false
182
+	 */
183
+	public boolean hasProperty(final String key) {
184
+		return getProperty(key) != null;
185
+	}
186
+	
187
+	/**
188
+	 * Get a Byte property from the config
189
+	 *
190
+	 * @param key key for property
191
+	 * @param fallback Value to return if key is not found
192
+	 * @return the requested property, or the fallback value if not defined
193
+	 */
194
+	public byte getByteProperty(final String key, final byte fallback) {
195
+		try {
196
+			return Byte.parseByte(getProperty(key, Byte.toString(fallback)));
197
+		} catch (NumberFormatException nfe) {
198
+			return fallback;
199
+		}
200
+	}
201
+	
202
+	/**
203
+	 * Set a Byte property in the config
204
+	 *
205
+	 * @param key key for property
206
+	 * @param value Value for property
207
+	 */
208
+	public void setByteProperty(final String key, final byte value) {
209
+		setProperty(key, Byte.toString(value));
210
+	}
211
+	
212
+	/**
213
+	 * Get a Short property from the config
214
+	 *
215
+	 * @param key key for property
216
+	 * @param fallback Value to return if key is not found
217
+	 * @return the requested property, or the fallback value if not defined
218
+	 */
219
+	public short getShortProperty(final String key, final short fallback) {
220
+		try {
221
+			return Short.parseShort(getProperty(key, Short.toString(fallback)));
222
+		} catch (NumberFormatException nfe) {
223
+			return fallback;
224
+		}
225
+	}
226
+	
227
+	/**
228
+	 * Set a Short property in the config
229
+	 *
230
+	 * @param key key for property
231
+	 * @param value Value for property
232
+	 */
233
+	public void setShortProperty(final String key, final short value) {
234
+		setProperty(key, Short.toString(value));
235
+	}
236
+	
237
+	/**
238
+	 * Get an integer property from the config
239
+	 *
240
+	 * @param key key for property
241
+	 * @param fallback Value to return if key is not found
242
+	 * @return the requested property, or the fallback value if not defined
243
+	 */
244
+	public int getIntProperty(final String key, final int fallback) {
245
+		try {
246
+			return Integer.parseInt(getProperty(key, Integer.toString(fallback)));
247
+		} catch (NumberFormatException nfe) {
248
+			return fallback;
249
+		}
250
+	}
251
+	
252
+	/**
253
+	 * Set an integer property in the config
254
+	 *
255
+	 * @param key key for property
256
+	 * @param value Value for property
257
+	 */
258
+	public void setIntProperty(final String key, final int value) {
259
+		setProperty(key, Integer.toString(value));
260
+	}
261
+	
262
+	/**
263
+	 * Get a Long property from the config
264
+	 *
265
+	 * @param key key for property
266
+	 * @param fallback Value to return if key is not found
267
+	 * @return the requested property, or the fallback value if not defined
268
+	 */
269
+	public long getLongProperty(final String key, final long fallback) {
270
+		try {
271
+			return Long.parseLong(getProperty(key, Long.toString(fallback)));
272
+		} catch (NumberFormatException nfe) {
273
+			return fallback;
274
+		}
275
+	}
276
+	
277
+	/**
278
+	 * Set a Long property in the config
279
+	 *
280
+	 * @param key key for property
281
+	 * @param value Value for property
282
+	 */
283
+	public void setLongProperty(final String key, final long value) {
284
+		setProperty(key, Long.toString(value));
285
+	}
286
+	
287
+	/**
288
+	 * Get a float property from the config
289
+	 *
290
+	 * @param key key for property
291
+	 * @param fallback Value to return if key is not found
292
+	 * @return the requested property, or the fallback value if not defined
293
+	 */
294
+	public float getFloatProperty(final String key, final float fallback) {
295
+		try {
296
+			return Float.parseFloat(getProperty(key, Float.toString(fallback)));
297
+		} catch (NumberFormatException nfe) {
298
+			return fallback;
299
+		}
300
+	}
301
+	
302
+	/**
303
+	 * Set a float property in the config
304
+	 *
305
+	 * @param key key for property
306
+	 * @param value Value for property
307
+	 */
308
+	public void setFloatProperty(final String key, final float value) {
309
+		setProperty(key, Float.toString(value));
310
+	}
311
+	
312
+	/**
313
+	 * Get a double property from the config
314
+	 *
315
+	 * @param key key for property
316
+	 * @param fallback Value to return if key is not found
317
+	 * @return the requested property, or the fallback value if not defined
318
+	 */
319
+	public double getDoubleProperty(final String key, final double fallback) {
320
+		try {
321
+			return Double.parseDouble(getProperty(key, Double.toString(fallback)));
322
+		} catch (NumberFormatException nfe) {
323
+			return fallback;
324
+		}
325
+	}
326
+	
327
+	/**
328
+	 * Set a double property in the config
329
+	 *
330
+	 * @param key key for property
331
+	 * @param value Value for property
332
+	 */
333
+	public void setDoubleProperty(final String key, final double value) {
334
+		setProperty(key, Double.toString(value));
335
+	}
336
+	
337
+	/**
338
+	 * Get a boolean property from the config
339
+	 *
340
+	 * @param key key for property
341
+	 * @param fallback Value to return if key is not found
342
+ 	 * @return the requested property, or the fallback value if not defined
343
+	 */
344
+	public boolean getBoolProperty(final String key, final boolean fallback) {
345
+		return Boolean.parseBoolean(getProperty(key, Boolean.toString(fallback)));
346
+	}
347
+	
348
+	/**
349
+	 * Set a Boolean property in the config
350
+	 *
351
+	 * @param key key for property
352
+	 * @param value Value for property
353
+	 */
354
+	public void setBoolProperty(final String key, final boolean value) {
355
+		setProperty(key, Boolean.toString(value));
356
+	}
357
+	
358
+	/**
359
+	 * Get a Char property from the config
360
+	 *
361
+	 * @param key key for property
362
+	 * @param fallback Value to return if key is not found
363
+	 * @return the requested property, or the fallback value if not defined
364
+	 */
365
+	public char getCharProperty(final String key, final char fallback) {
366
+		final String res = getProperty(key, Character.toString(fallback));
367
+		if (res == null || res.isEmpty()) {
368
+			return fallback;
369
+		} else {
370
+			return res.charAt(0);
371
+		}
372
+	}
373
+	
374
+	/**
375
+	 * Set a Char property in the config
376
+	 *
377
+	 * @param key key for property
378
+	 * @param value Value for property
379
+	 */
380
+	public void setCharProperty(final String key, final char value) {
381
+		setProperty(key, Character.toString(value));
382
+	}
383
+	
384
+	/**
385
+	 * Get a List property from the config.
386
+	 *
387
+	 * @param key key for property
388
+	 * @param fallback List to return if key is not found
389
+	 * @return the requested property, or the fallback value if not defined
390
+	 */
391
+	public List<String> getListProperty(final String key, final List<String> fallback) {
392
+		final String res = getProperty(key, "");
393
+		if (res == null || res.isEmpty()) {
394
+			return fallback;
395
+		} else {
396
+			final String bits[] = res.split("\n");
397
+			final ArrayList<String> result = new ArrayList<String>();
398
+			for (String bit : bits) {
399
+				result.add(bit);
400
+			}
401
+			return result;
402
+		}
403
+	}
404
+	
405
+	/**
406
+	 * Set a List property in the config
407
+	 *
408
+	 * @param key key for property
409
+	 * @param value Value for property
410
+	 */
411
+	public void setListProperty(final String key, final List<String> value) {
412
+		final StringBuilder val = new StringBuilder();
413
+		final String LF = "\n";
414
+		boolean first = true;
415
+		for (String bit : value) {
416
+			if (first) { first = false; } else { val.append(LF); }
417
+			val.append(bit);
418
+		}
419
+		setProperty(key, val.toString());
420
+	}
421
+}

+ 27
- 0
src/com/dmdirc/addons/scriptplugin/plugin.config 查看文件

@@ -0,0 +1,27 @@
1
+keysections:
2
+  metadata
3
+  version
4
+  updates
5
+  defaults
6
+
7
+metadata:
8
+  mainclass=com.dmdirc.addons.scriptplugin.ScriptPlugin
9
+  author=Shane <shane@dmdirc.com>
10
+  description=Allows using javascript scripts with DMDirc
11
+  name=script
12
+  nicename=Scripting Plugin
13
+
14
+version:
15
+  number=3
16
+  friendly=0.3
17
+
18
+updates:
19
+  id=33
20
+
21
+defaults:
22
+  eval.showStackTrace=false
23
+
24
+provides:
25
+  script command
26
+  scripting feature
27
+  javascript script

正在加载...
取消
保存