source: trunk/src/org/expeditee/actions/Actions.java@ 41

Last change on this file since 41 was 41, checked in by ra33, 16 years ago

Yellow highlighting for deleting text ranges wasnt working.

Fixed a couple things for the left and right mouse button shortcut for formatting

File size: 15.8 KB
Line 
1package org.expeditee.actions;
2
3import java.io.File;
4import java.lang.reflect.Constructor;
5import java.lang.reflect.Method;
6import java.lang.reflect.Modifier;
7import java.net.URL;
8import java.util.ArrayList;
9import java.util.Enumeration;
10import java.util.HashMap;
11import java.util.LinkedList;
12import java.util.List;
13import java.util.jar.JarFile;
14import java.util.zip.ZipEntry;
15
16import org.expeditee.agents.Agent;
17import org.expeditee.gui.DisplayIO;
18import org.expeditee.gui.Frame;
19import org.expeditee.gui.FrameGraphics;
20import org.expeditee.gui.FrameIO;
21import org.expeditee.gui.FrameUtils;
22import org.expeditee.io.Conversion;
23import org.expeditee.io.Logger;
24import org.expeditee.items.Item;
25import org.expeditee.items.ItemUtils;
26
27/**
28 * The Action class is used to launch KMS Actions as well as launching JAGs.
29 *
30 * This class checks all class files in the same directory, and reads in and
31 * adds all the methods from them. The methods are stored in a Hashtable so that
32 * the lowercase method names can be mapped to the correctly capatilized method
33 * names (to provide case-insensitivity)
34 */
35public class Actions {
36
37 // the currently running agent (if there is one)
38 private static Agent _Agent = null;
39
40 // maps lower case method names to the method
41 private static HashMap<String, Method> _Actions = new HashMap<String, Method>();
42
43 // maps lower case JAG class names to capitalized JAG names
44 private static HashMap<String, String> _JAGs = new HashMap<String, String>();
45
46 public static final String ROOT_PACKAGE = "org.expeditee.";
47
48 // Package and class file locations
49 private static final String ACTIONS_PACKAGE = ROOT_PACKAGE + "actions.";
50
51 private static final String AGENTS_PACKAGE = ROOT_PACKAGE + "agents.";
52
53 private static final String NAVIGATIONS_CLASS = ROOT_PACKAGE + "actions.NavigationActions";
54
55 public static Class[] getClasses(String pckgname)
56 throws ClassNotFoundException {
57 ArrayList<Class> classes = new ArrayList<Class>();
58 // Get a File object for the package
59 File directory = null;
60 String path = pckgname.replace('.', '/');
61 try {
62 ClassLoader cld = Thread.currentThread().getContextClassLoader();
63 if (cld == null) {
64 throw new ClassNotFoundException("Can't get class loader.");
65 }
66 URL resource = cld.getResource(path);
67 if (resource == null) {
68 throw new ClassNotFoundException("No resource for " + path);
69 }
70 directory = new File(resource.getFile());
71 } catch (NullPointerException x) {
72 throw new ClassNotFoundException(pckgname + " (" + directory
73 + ") does not appear to be a valid package");
74 }
75
76 int splitPoint = directory.getPath().indexOf('!');
77 if (splitPoint > 0) {
78 try {
79 String jarName = directory.getPath().substring(
80 "file:".length(), splitPoint);
81 // Windows HACK
82 if (jarName.indexOf(":") >= 0)
83 jarName = jarName.substring(1);
84
85 if (jarName.indexOf("%20") > 0) {
86 jarName = jarName.replace("%20", " ");
87 }
88
89 JarFile jarFile = new JarFile(jarName);
90
91 Enumeration entries = jarFile.entries();
92 while (entries.hasMoreElements()) {
93 ZipEntry entry = (ZipEntry) entries.nextElement();
94 String className = entry.getName();
95 if (className.startsWith(path)) {
96 if (className.endsWith(".class")
97 && !className.contains("$")) {
98 classes.add(Class.forName(className.substring(0,
99 className.length() - 6).replace('/', '.')));
100 }
101 }
102 }
103 jarFile.close();
104
105 } catch (Exception e) {
106 e.printStackTrace();
107 }
108
109 } else {
110
111 if (directory.exists()) {
112 // Get the list of the files contained in the package
113 String[] files = directory.list();
114 for (int i = 0; i < files.length; i++) {
115 // we are only interested in .class files
116 if (files[i].endsWith(".class") && !files[i].contains("$")
117 && !files[i].equals("Actions.class")) {
118 // removes the .class extension
119 classes
120 .add(Class.forName(pckgname
121 + files[i].substring(0, files[i]
122 .length() - 6)));
123 }
124 }
125 } else {
126 throw new ClassNotFoundException(pckgname + " (" + directory
127 + ") does not appear to be a valid package");
128 }
129 }
130 Class[] classesA = new Class[classes.size()];
131 classes.toArray(classesA);
132 return classesA;
133 }
134
135 /**
136 * Clears out the Action and JAG Hashtables and refills them. Normally this
137 * is only called once when the system starts.
138 */
139 public static void Init() {
140 Class[] classes;
141 try {
142 classes = getClasses(AGENTS_PACKAGE);
143
144 for (int i = 0; i < classes.length; i++) {
145 String name = classes[i].getSimpleName();
146 // maps lower case name to correct capitalised name
147 _JAGs.put(name.toLowerCase(), name);
148 }
149 } catch (Exception e) {
150 System.out
151 .println("You must have Java 1.5 or higher to run Expeditee");
152 System.out.println(e.getMessage());
153 }
154 try {
155 classes = getClasses(ACTIONS_PACKAGE);
156
157 for (int i = 0; i < classes.length; i++) {
158 String name = classes[i].getSimpleName();
159 // read in all the methods from the class
160 try {
161 // System.out.println(name);
162 LoadMethods(Class.forName(ACTIONS_PACKAGE + name));
163 } catch (ClassNotFoundException e) {
164 Logger.Log(e);
165 e.printStackTrace();
166 }
167 }
168 } catch (Exception e) {
169 System.out.println(e.getMessage());
170 }
171 }
172
173 /**
174 * Loads all the Methods that meet the requirements checked by MethodCheck
175 * into the hashtable.
176 *
177 * @param c
178 * The Class to load the Methods from.
179 */
180 private static void LoadMethods(Class c) {
181 // list of methods to test
182 Method[] toLoad = c.getMethods();
183
184 for (Method m : toLoad) {
185 // only allow methods with the right modifiers
186 if (MethodCheck(m)) {
187 if (!(_Actions.containsKey(m.getName().toLowerCase())))
188 _Actions.put(m.getName().toLowerCase(), m);
189 else {
190 int i = 0;
191 while (_Actions.containsKey(m.getName().toLowerCase() + i))
192 i++;
193
194 _Actions.put(m.getName().toLowerCase() + i, m);
195 }
196
197 }
198 }
199 }
200
201 /**
202 * Checks if the given Method corresponds to the restrictions of Action
203 * commands, namely: Declared (not inherited), Public, and Static, with a
204 * void return type.
205 *
206 * @param m
207 * The Method to check
208 * @return True if the Method meets the above conditions, false otherwise.
209 */
210 private static boolean MethodCheck(Method m) {
211 // the return type must be void
212 if (m.getReturnType() != void.class)
213 return false;
214
215 int mods = m.getModifiers();
216
217 // check the method is declared (not inherited)
218 if ((mods & Method.DECLARED) != Method.DECLARED)
219 return false;
220
221 // check the method is public
222 if ((mods & Modifier.PUBLIC) != Modifier.PUBLIC)
223 return false;
224
225 // check the method is static
226 if ((mods & Modifier.STATIC) != Modifier.STATIC)
227 return false;
228
229 // if we have not returned yet, then the tests have all passed
230 return true;
231 }
232
233 /**
234 * Performs the given KMS action command. The source Frame and Item are
235 * given because they are required by some actions. Note that the source
236 * frame does not have to be the Item's parent Frame.
237 *
238 * @param source
239 * The Frame that the action should apply to
240 * @param launcher
241 * The Item that has the action assigned to it
242 * @param command
243 * The action to perform
244 */
245 public static void PerformAction(Frame source, Item launcher, String command) {
246 if (!command.equalsIgnoreCase("Restore"))
247 FrameIO.SaveFrame(source, false);
248
249 // separate method name and parameter names
250 String mname = getName(command);
251 if (command.length() > mname.length())
252 command = command.substring(mname.length() + 1);
253 else
254 command = "";
255
256 // Strip off the @ from annotation items
257 if (mname.startsWith("@"))
258 mname = mname.substring(1);
259
260 mname = mname.trim().toLowerCase();
261
262 // check for protection on frame
263 if (ItemUtils.ContainsTag(source.getItems(), "@No" + mname)) {
264 FrameGraphics.DisplayMessage("Frame is protected by @No" + mname
265 + " tag.");
266 return;
267 }
268
269 // retrieve methods that match the name
270 Method toRun = _Actions.get(mname);
271
272 // if this is not the name of a method, it may be the name of an agent
273 if (toRun == null) {
274 LaunchAgent(mname, command, source);
275 return;
276 }
277
278 // Need to save the frame if we are navigating away from it so we dont
279 // loose changes
280 if (toRun.getDeclaringClass().getName().equals(
281 NAVIGATIONS_CLASS)) {
282 FrameIO.SaveFrame(DisplayIO.getCurrentFrame());
283 }
284
285 // if there are duplicate methods with the same name
286 List<Method> possibles = new LinkedList<Method>();
287 possibles.add(toRun);
288 int i = 0;
289 while (_Actions.containsKey(mname + i)) {
290 possibles.add(_Actions.get(mname + i));
291 i++;
292 }
293
294 for (Method possible : possibles) {
295
296 // try first with the launching item as a parameter
297
298 // run method
299 try {
300 // convert parameters to objects and get the method to invoke
301 Object[] parameters = CreateObjects(possible, source, launcher,
302 command);
303
304 possible.invoke(null, parameters);
305 return;
306 } catch (Exception e) {
307 Logger.Log(e);
308 e.printStackTrace();
309 }
310 }
311 }
312
313 /**
314 * Launches an agent with the given name, and passes in the given parameters
315 *
316 * @param name
317 * The name of the JAG to load
318 * @param parameters
319 * The parameters to pass to the JAG
320 * @param source
321 * The starting Frame that the JAG is being launched on
322 */
323 private static void LaunchAgent(String name, String parameters, Frame source) {
324 // save the current frame (if necesssary)
325 FrameUtils.LeavingFrame(source);
326
327 try {
328 // check for stored capitalisation
329 if (_JAGs.containsKey(name.toLowerCase())) {
330 name = _JAGs.get(name.toLowerCase());
331 } else if (name.toLowerCase().endsWith("tree")) {
332 parameters = name.substring(0, name.length() - "tree".length())
333 + " " + parameters;
334 name = "WriteTree";
335 } else if (name.toLowerCase().endsWith("frame")) {
336 parameters = name
337 .substring(0, name.length() - "frame".length())
338 + " " + parameters;
339 name = "WriteFrame";
340 }
341
342 // load the JAG class
343 Class agentClass = Class.forName(AGENTS_PACKAGE + name);
344
345 // get the constructor for the JAG class
346 Constructor con = null;
347 Constructor constructors[] = agentClass.getConstructors();
348 Object[] params = null;
349
350 // determine correct parameters for constructor
351 for (Constructor c : constructors) {
352 if (parameters.length() > 0
353 && c.getParameterTypes().length == 1) {
354 con = c;
355 params = new String[1];
356 params[0] = parameters;
357 break;
358 } else if (c.getParameterTypes().length == 0 && con == null)
359 con = c;
360 }
361
362 // if there is no constructor, return
363 if (con == null) {
364 FrameGraphics.DisplayMessage("Invalid parametres for agent.");
365 //System.out.println("Constructor not found...");
366 return;
367 }
368
369 // create the JAG
370 _Agent = (Agent) con.newInstance(params);
371
372 Thread t = new Thread(_Agent);
373 t.setPriority(Thread.MIN_PRIORITY);
374
375 // check for errors during initialisation
376 if (!_Agent.initialise(source)) {
377 FrameGraphics.ErrorMessage("Error initialising agent: " + name);
378 return;
379 }
380
381 _Agent.setStartFrame(source);
382
383 t.start();
384
385 // if there is a completion frame, then display it to the user
386 if (_Agent.hasResultFrame()) {
387 Frame next = _Agent.getResultFrame();
388 FrameUtils.DisplayFrame(next, true);
389 }
390
391 } catch (ClassNotFoundException cnf) {
392 FrameGraphics.ErrorMessage("Error: '" + name
393 + "' is not an action statement or Agent.");
394 } catch (Exception e) {
395 FrameGraphics.ErrorMessage("Error creating Agent: '" + name + "'");
396 System.out.println("Agent set to Null.");
397 _Agent = null;
398 e.printStackTrace();
399 Logger.Log(e);
400 }
401
402 return;
403 }
404
405 /**
406 * Used to determine if the previously launched agent is still executing.
407 *
408 * @return True if the last Agent is still executing, False otherwise.
409 */
410 public static boolean isAgentRunning() {
411 if (_Agent != null)
412 return _Agent.isRunning();
413
414 return false;
415 }
416
417 /**
418 * Stops the currently running Agent (If there is one) by calling
419 * Agent.stop(). Note: This may not stop the Agent immediately, but the
420 * Agent should terminate as soon as it is safe to do so.
421 */
422 public static void stopAgent() {
423 if (_Agent != null) {
424 FrameGraphics.DisplayMessage("Stopping Agent...");
425 _Agent.stop();
426
427 // while (_Agent.isRunning())
428 // ;
429 // FrameGraphics.DisplayMessage("Agent Stopped.");
430 }
431 }
432
433 public static void interruptAgent() {
434 if (_Agent != null) {
435 _Agent.interrupt();
436 }
437 }
438
439 /**
440 * Converts the given String of values into an array of Objects
441 *
442 * @param launcher
443 * The Item used to launch the action, it may be required as a
444 * parameter
445 * @param values
446 * A list of space separated String values to convert to objects
447 * @return The created array of Objects
448 */
449 public static Object[] CreateObjects(Method method, Frame source,
450 Item launcher, String values) {
451 // The parameter types that should be created from the given String
452 Class[] paramTypes = method.getParameterTypes();
453
454 // if the method has no parameters
455 if (paramTypes.length == 0)
456 return new Object[0];
457
458 Object[] objects = new Object[paramTypes.length];
459 int ind = 0;
460
461 // if the first class in the list is a frame or item, it is the source
462 // or launcher
463 // length must be at least one if we are still running
464 if (paramTypes[0] == Frame.class) {
465 objects[0] = source;
466 ind = 1;
467 } else if (paramTypes[0] == Item.class) {
468 objects[0] = launcher;
469 ind = 1;
470 }
471
472 // if the first class was a frame or item, the second class might be the
473 // other
474 if (paramTypes.length > 1 && objects[0] != null) {
475 if (paramTypes[1] == Frame.class) {
476 objects[1] = source;
477 ind = 2;
478 } else if (paramTypes[1] == Item.class) {
479 objects[1] = launcher;
480 ind = 2;
481 }
482 }
483
484 String param;
485 // convert the rest of the objects
486 for (; ind < objects.length; ind++) {
487 // strip off the next value
488 param = ParseValue(values);
489 values = RemainingParams(values);
490
491 // check if its the last param and combine
492 if (values.length() > 0 && ind == objects.length - 1)
493 param = param.trim() + ' ' + values.trim();
494
495 // convert the value to an object
496 Object o = Conversion.Convert(paramTypes[ind], param);
497 objects[ind] = o;
498 }
499
500 return objects;
501 }
502
503 /**
504 * Returns a string containing the remaining params after ignoring the first
505 * one.
506 *
507 * @param params
508 * a space sparated list of N parameters
509 * @return the remaining N - 1 parameters
510 */
511 private static String RemainingParams(String params) {
512 if (params.length() == 0)
513 return null;
514
515 // remove leading and trailing spaces
516 params = params.trim();
517
518 // if there are no more parameters, we are done
519 if (params.indexOf(" ") < 0) {
520 return "";
521 }
522
523 // Check if we have a string parameter
524 if (params.charAt(0) == '"') {
525 return params.substring(params.indexOf('"', 1) + 1);
526 }
527
528 return params.substring(params.indexOf(" "));
529 }
530
531 /**
532 * Returns the first value in the space separated String of parameters
533 * passed in. Strings are enclosed in double quotes.
534 *
535 * @param params
536 * The String of space separated values
537 * @return The first value in the String
538 */
539 private static String ParseValue(String params) {
540 if (params.length() == 0)
541 return null;
542
543 // remove leading and trailing spaces
544 String param = params.trim();
545
546 // if there are no more parameters, we are done
547 if (param.indexOf(" ") < 0) {
548 return param;
549 }
550
551 // Check if we have a string parameter
552 if (param.charAt(0) == '"') {
553 return param.substring(1, param.indexOf('"', 1));
554 }
555
556 return param.substring(0, param.indexOf(" "));
557 }
558
559 /**
560 * Separates the name of the given command from any parameters and returns
561 * them
562 *
563 * @param command
564 * The String to separate out the Action or Agent name from
565 * @return The name of the Action of Agent with parameters stripped off
566 */
567 private static String getName(String command) {
568 if (command.indexOf(" ") < 0)
569 return command;
570
571 return command.substring(0, command.indexOf(" "));
572 }
573}
Note: See TracBrowser for help on using the repository browser.