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

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

Added lots of stuff

File size: 16.2 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
54 + "actions.NavigationActions";
55
56 public static Class[] getClasses(String pckgname)
57 throws ClassNotFoundException {
58 ArrayList<Class> classes = new ArrayList<Class>();
59 // Get a File object for the package
60 File directory = null;
61 String path = pckgname.replace('.', '/');
62 try {
63 ClassLoader cld = Thread.currentThread().getContextClassLoader();
64 if (cld == null) {
65 throw new ClassNotFoundException("Can't get class loader.");
66 }
67 URL resource = cld.getResource(path);
68 if (resource == null) {
69 throw new ClassNotFoundException("No resource for " + path);
70 }
71 directory = new File(resource.getFile());
72 } catch (NullPointerException x) {
73 throw new ClassNotFoundException(pckgname + " (" + directory
74 + ") does not appear to be a valid package");
75 }
76
77 int splitPoint = directory.getPath().indexOf('!');
78 if (splitPoint > 0) {
79 try {
80 String jarName = directory.getPath().substring(
81 "file:".length(), splitPoint);
82 // Windows HACK
83 if (jarName.indexOf(":") >= 0)
84 jarName = jarName.substring(1);
85
86 if (jarName.indexOf("%20") > 0) {
87 jarName = jarName.replace("%20", " ");
88 }
89
90 JarFile jarFile = new JarFile(jarName);
91
92 Enumeration entries = jarFile.entries();
93 while (entries.hasMoreElements()) {
94 ZipEntry entry = (ZipEntry) entries.nextElement();
95 String className = entry.getName();
96 if (className.startsWith(path)) {
97 if (className.endsWith(".class")
98 && !className.contains("$")) {
99 classes.add(Class.forName(className.substring(0,
100 className.length() - 6).replace('/', '.')));
101 }
102 }
103 }
104 jarFile.close();
105
106 } catch (Exception e) {
107 e.printStackTrace();
108 }
109
110 } else {
111
112 if (directory.exists()) {
113 // Get the list of the files contained in the package
114 String[] files = directory.list();
115 for (int i = 0; i < files.length; i++) {
116 // we are only interested in .class files
117 if (files[i].endsWith(".class") && !files[i].contains("$")
118 && !files[i].equals("Actions.class")) {
119 // removes the .class extension
120 classes
121 .add(Class.forName(pckgname
122 + files[i].substring(0, files[i]
123 .length() - 6)));
124 }
125 }
126 } else {
127 throw new ClassNotFoundException(pckgname + " (" + directory
128 + ") does not appear to be a valid package");
129 }
130 }
131 Class[] classesA = new Class[classes.size()];
132 classes.toArray(classesA);
133 return classesA;
134 }
135
136 /**
137 * Clears out the Action and JAG Hashtables and refills them. Normally this
138 * is only called once when the system starts.
139 */
140 public static void Init() {
141 Class[] classes;
142 try {
143 classes = getClasses(AGENTS_PACKAGE);
144
145 for (int i = 0; i < classes.length; i++) {
146 String name = classes[i].getSimpleName();
147 // maps lower case name to correct capitalised name
148 _JAGs.put(name.toLowerCase(), name);
149 }
150 } catch (Exception e) {
151 System.out
152 .println("You must have Java 1.5 or higher to run Expeditee");
153 System.out.println(e.getMessage());
154 }
155 try {
156 classes = getClasses(ACTIONS_PACKAGE);
157
158 for (int i = 0; i < classes.length; i++) {
159 String name = classes[i].getSimpleName();
160 // read in all the methods from the class
161 try {
162 // System.out.println(name);
163 LoadMethods(Class.forName(ACTIONS_PACKAGE + name));
164 } catch (ClassNotFoundException e) {
165 Logger.Log(e);
166 e.printStackTrace();
167 }
168 }
169 } catch (Exception e) {
170 System.out.println(e.getMessage());
171 }
172 }
173
174 /**
175 * Loads all the Methods that meet the requirements checked by MethodCheck
176 * into the hashtable.
177 *
178 * @param c
179 * The Class to load the Methods from.
180 */
181 private static void LoadMethods(Class c) {
182 // list of methods to test
183 Method[] toLoad = c.getMethods();
184
185 for (Method m : toLoad) {
186 // only allow methods with the right modifiers
187 if (MethodCheck(m)) {
188 if (!(_Actions.containsKey(m.getName().toLowerCase())))
189 _Actions.put(m.getName().toLowerCase(), m);
190 else {
191 int i = 0;
192 while (_Actions.containsKey(m.getName().toLowerCase() + i))
193 i++;
194
195 _Actions.put(m.getName().toLowerCase() + i, m);
196 }
197
198 }
199 }
200 }
201
202 /**
203 * Checks if the given Method corresponds to the restrictions of Action
204 * commands, namely: Declared (not inherited), Public, and Static, with a
205 * void return type.
206 *
207 * @param m
208 * The Method to check
209 * @return True if the Method meets the above conditions, false otherwise.
210 */
211 private static boolean MethodCheck(Method m) {
212 // the return type must be void
213 if (m.getReturnType() != void.class)
214 return false;
215
216 int mods = m.getModifiers();
217
218 // check the method is declared (not inherited)
219 if ((mods & Method.DECLARED) != Method.DECLARED)
220 return false;
221
222 // check the method is public
223 if ((mods & Modifier.PUBLIC) != Modifier.PUBLIC)
224 return false;
225
226 // check the method is static
227 if ((mods & Modifier.STATIC) != Modifier.STATIC)
228 return false;
229
230 // if we have not returned yet, then the tests have all passed
231 return true;
232 }
233
234 /**
235 * Performs the given action command. The source Frame and Item are given
236 * because they are required by some actions. Note that the source frame
237 * does not have to be the Item's parent Frame.
238 *
239 * @param source
240 * The Frame that the action should apply to
241 * @param launcher
242 * The Item that has the action assigned to it
243 * @param command
244 * The action to perform
245 */
246 public static void PerformAction(Frame source, Item launcher, String command) {
247 if (!command.equalsIgnoreCase("Restore"))
248 FrameIO.SaveFrame(source, false);
249
250 // separate method name and parameter names
251 String mname = getName(command);
252 if (command.length() > mname.length())
253 command = command.substring(mname.length() + 1);
254 else
255 command = "";
256
257 // Strip off the @ from annotation items
258 if (mname.startsWith("@"))
259 mname = mname.substring(1);
260
261 mname = mname.trim().toLowerCase();
262
263 // check for protection on frame
264 if (ItemUtils.ContainsTag(source.getItems(), "@No" + mname)) {
265 FrameGraphics.DisplayMessage("Frame is protected by @No" + mname
266 + " tag.");
267 return;
268 }
269
270 // retrieve methods that match the name
271 Method toRun = _Actions.get(mname);
272
273 // if this is not the name of a method, it may be the name of an agent
274 if (toRun == null) {
275 LaunchAgent(mname, command, source);
276 return;
277 }
278
279 // Need to save the frame if we are navigating away from it so we dont
280 // loose changes
281 if (toRun.getDeclaringClass().getName().equals(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 = values;
485 // convert the rest of the objects
486 for (; ind < objects.length; ind++) {
487 // check if its the last param and combine
488 if (values.length() > 0 && ind == objects.length - 1) {
489 param = values.trim();
490 // check if its a string
491 if (param.length() > 0 && param.charAt(0) == '"'){
492 int endOfString = param.indexOf('"', 1);
493 if (endOfString > 0){
494 param = param.substring(0,endOfString);
495 }
496 }
497 } else {// strip off the next value
498 param = ParseValue(values);
499 values = RemainingParams(values);
500 }
501 // convert the value to an object
502 Object o = Conversion.Convert(paramTypes[ind], param);
503 objects[ind] = o;
504 }
505
506 return objects;
507 }
508
509 /**
510 * Returns a string containing the remaining params after ignoring the first
511 * one.
512 *
513 * @param params
514 * a space sparated list of N parameters
515 * @return the remaining N - 1 parameters
516 */
517 public static String RemainingParams(String params) {
518 if (params.length() == 0)
519 return null;
520
521 // remove leading and trailing spaces
522 params = params.trim();
523
524 // if there are no more parameters, we are done
525 if (params.indexOf(" ") < 0) {
526 return "";
527 }
528
529 // Check if we have a string parameter
530 if (params.charAt(0) == '"') {
531 int endOfString = params.indexOf('"', 1);
532 if (endOfString > 0){
533 if (endOfString > params.length())
534 return "";
535 return params.substring(endOfString + 1).trim();
536 }
537 }
538
539 return params.substring(params.indexOf(" ")).trim();
540 }
541
542 /**
543 * Returns the first value in the space separated String of parameters
544 * passed in. Strings are enclosed in double quotes.
545 *
546 * @param params
547 * The String of space separated values
548 * @return The first value in the String
549 */
550 public static String ParseValue(String params) {
551 if (params.length() == 0)
552 return null;
553
554 // remove leading and trailing spaces
555 String param = params.trim();
556
557 // Check if we have a string parameter
558 if (param.charAt(0) == '"') {
559 int endOfString = param.indexOf('"', 1);
560 if (endOfString > 0)
561 return param.substring(1, endOfString);
562 }
563
564 // if there are no more parameters, we are done
565 if (param.indexOf(" ") < 0) {
566 return param;
567 }
568
569 return param.substring(0, param.indexOf(" "));
570 }
571
572 /**
573 * Separates the name of the given command from any parameters and returns
574 * them
575 *
576 * @param command
577 * The String to separate out the Action or Agent name from
578 * @return The name of the Action of Agent with parameters stripped off
579 */
580 private static String getName(String command) {
581 if (command.indexOf(" ") < 0)
582 return command;
583
584 return command.substring(0, command.indexOf(" "));
585 }
586}
Note: See TracBrowser for help on using the repository browser.