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

Last change on this file since 534 was 534, checked in by csl14, 11 years ago

startBrowser and startBrowserNewFrame now create JfxBrowser widget instead of the lobobrowser widget

File size: 35.8 KB
Line 
1package org.expeditee.actions;
2
3import java.awt.GraphicsEnvironment;
4import java.io.File;
5import java.io.IOException;
6import java.lang.reflect.Constructor;
7import java.lang.reflect.Method;
8import java.lang.reflect.Modifier;
9import java.net.URL;
10import java.net.URI;
11import java.net.URLDecoder;
12import java.net.JarURLConnection;
13import java.rmi.UnexpectedException;
14import java.util.ArrayList;
15import java.util.Collection;
16import java.util.Enumeration;
17import java.util.HashMap;
18import java.util.LinkedList;
19import java.util.List;
20import java.util.Set;
21import java.util.jar.JarEntry;
22import java.util.jar.JarFile;
23import java.util.zip.ZipEntry;
24
25
26import org.expeditee.agents.Agent;
27
28import org.expeditee.gui.DisplayIO;
29import org.expeditee.gui.Frame;
30import org.expeditee.gui.FrameGraphics;
31import org.expeditee.gui.FrameIO;
32import org.expeditee.gui.FrameUtils;
33import org.expeditee.gui.FreeItems;
34import org.expeditee.gui.MessageBay;
35import org.expeditee.gui.UserSettings;
36import org.expeditee.io.Conversion;
37import org.expeditee.items.Item;
38import org.expeditee.items.ItemUtils;
39import org.expeditee.items.Text;
40import org.expeditee.simple.SString;
41import org.expeditee.stats.Logger;
42
43/**
44 * The Action class is used to launch Actions and Agents.
45 *
46 * This class checks all class files in the same directory, and reads in and
47 * adds all the methods from them. The methods are stored in a Hashtable so that
48 * the lowercase method names can be mapped to the correctly capatilized method
49 * names (to provide case-insensitivity)
50 *
51 * When adding an action to a class in the actions folder the following must be
52 * considered: <li>If the first parameter is of type Frame, the current frame
53 * will be passed as a parameter. <li>If the next param is of type Item the item
54 * on the end of the cursor will be passed or the item that was clicked to
55 * execute the action if nothing is on the end of the cursor. current frame or
56 * item.</li> <li>If there are multiple overloads for the same method they
57 * should be declared in order of the methods with the most parameteres to least
58 * parameters.</li>
59 */
60public class Actions {
61
62 private static final String INVALID_PARAMETERS_ERROR = "Invalid parameters for agent: "; //$NON-NLS-1$
63
64 // the currently running agent (if there is one)
65 private static Agent _Agent = null;
66
67 // maps lower case method names to the method
68 private static HashMap<String, Method> _Actions = new HashMap<String, Method>();
69
70 // map lower case fonts to capitalized fonts
71 private static HashMap<String, String> _Fonts = new HashMap<String, String>();
72
73 // maps lower case JAG class names to capitalized JAG full class names
74 private static HashMap<String, String> _JAGs = new HashMap<String, String>();
75
76 // maps lower case IW class names to capitalized IW names
77 private static HashMap<String, String> _IWs = new HashMap<String, String>();
78
79 public static final String ROOT_PACKAGE = "org.expeditee.";
80
81 // Package and class file locations
82 private static final String ACTIONS_PACKAGE = ROOT_PACKAGE + "actions.";
83
84 private static final String AGENTS_PACKAGE = ROOT_PACKAGE + "agents.";
85
86 private static final String WIDGET_PACKAGE = ROOT_PACKAGE
87 + "items.widgets.";
88
89 private static final String CHARTS_PACKAGE = ROOT_PACKAGE
90 + "items.widgets.charts.";
91
92 private static final String NAVIGATIONS_CLASS = ROOT_PACKAGE
93 + "actions.NavigationActions";
94
95
96
97 // The following is adapted from:
98 // http://stackoverflow.com/questions/1456930/how-do-i-read-all-classes-from-a-java-package-in-the-classpath
99
100 public static Class<?>[] getClassesNew(String packageName)
101 throws ClassNotFoundException {
102
103 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
104
105 ArrayList<String> names = new ArrayList<String>();;
106 final ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
107
108 String realPackageName = packageName;
109 packageName = packageName.replace(".", "/");
110 URL packageURL = classLoader.getResource(packageName);
111
112 if(packageURL.getProtocol().equals("jar")) {
113
114 // build jar file name, then loop through zipped entries
115
116 String jarFileNameUndecoded = packageURL.getFile();
117
118 try {
119 String jarFileName = URLDecoder.decode(jarFileNameUndecoded, "UTF-8");
120
121
122 JarURLConnection ju_connection=(JarURLConnection)packageURL.openConnection();
123 JarFile jf =ju_connection.getJarFile();
124
125 Enumeration<JarEntry> jarEntries = jf.entries();
126 while(jarEntries.hasMoreElements()) {
127 String entryName = jarEntries.nextElement().getName();
128
129 if (entryName.startsWith(packageName)) {
130
131 if (entryName.endsWith(".class") && !entryName.contains("$")) {
132
133 // Deal with situation where the class found might be a further sub-package
134 // e.g. for "org.expeditee.action"
135 // there is: org/expeditee/action/widgets/Chart.class
136 // which would produce a value of 'entryName' as widgets/Chart.class
137
138 entryName = entryName.substring(0,entryName.length() - 6); // 6 = '.class'
139 entryName = entryName.replace('/', '.');
140
141 names.add(entryName);
142 classes.add(Class.forName(entryName));
143 }
144 }
145 }
146 }
147 catch (Exception e) {
148 System.err.println("Failed to decode jar file: " + jarFileNameUndecoded);
149 e.printStackTrace();
150 }
151 }
152 else {
153 // loop through files in classpath
154
155 String packageURLString = packageURL.toString();
156 try {
157 URI uri = new URI(packageURLString);
158 File folder = new File(uri.getPath());
159
160 // won't work with path which contains blank (%20)
161 // File folder = new File(packageURL.getFile());
162 File[] contenuti = folder.listFiles();
163
164 for (File actual: contenuti) {
165 String entryName = realPackageName + actual.getName();
166
167 if (entryName.endsWith(".class") && !entryName.contains("$")) {
168 entryName = entryName.substring(0, entryName.lastIndexOf('.'));
169 entryName.replace('/', '.');
170
171 names.add(entryName);
172 classes.add(Class.forName(entryName));
173 }
174 }
175 }
176 catch (Exception e) {
177 System.err.println("Failed to process file: " + packageURLString);
178 e.printStackTrace();
179 }
180 }
181
182 Class<?>[] classesA = new Class<?>[classes.size()];
183 classes.toArray(classesA);
184 return classesA;
185 }
186
187 public static Class<?>[] getClasses(final String pckgname)
188 throws ClassNotFoundException {
189
190 final ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
191
192 // Must be a forward slash for loading resources
193 final String packagePath = pckgname.replace('.', '/');
194
195 if (System.getProperty("eclipse.expeditee.home") == null) {
196 final ClassLoader cld = Thread.currentThread().getContextClassLoader();
197 if (cld == null) {
198 throw new ClassNotFoundException("Can't get class loader.");
199 }
200 URL resource = null;
201 try {
202 final Enumeration<URL> resources = cld.getResources(packagePath);
203 while(resources.hasMoreElements()) {
204 URL url = resources.nextElement();
205 resource = url;
206 }
207 } catch (IOException e) {
208 System.err.println( "A IO Error has occured when trying to use the ContextClassLoader"
209 + System.getProperty("line.separator")
210 + "Are you running from within Eclipse? (or just not with Jar) Then make sure your"
211 + " 'eclipse.expeditee.home' property is set correctly. It is currently: '"
212 + System.getProperty("eclipse.expeditee.home") + "'" + System.getProperty("line.separator")
213 + "You can set it by adding a VM argument. "
214 + "Example: -Declipse.expeditee.home=D:\\Desktop\\Research\\expeditee-svn");
215 e.printStackTrace();
216 }
217 if (resource == null) {
218 throw new ClassNotFoundException("No resource for " + packagePath);
219 }
220 final File directory = new File(resource.getFile());
221
222 final int splitPoint = directory.getPath().indexOf('!');
223 if(splitPoint > 0) {
224 String jarName = directory.getPath().substring("file:".length(),
225 splitPoint);
226 // Windows HACK
227 if (jarName.indexOf(":") >= 0)
228 jarName = jarName.substring(1);
229
230 if (jarName.indexOf("%20") > 0) {
231 jarName = jarName.replace("%20", " ");
232 }
233 // System.out.println("JarName:" + jarName);
234 try {
235 final JarFile jarFile = new JarFile(jarName);
236 final Enumeration<?> entries = jarFile.entries();
237 while (entries.hasMoreElements()) {
238 final ZipEntry entry = (ZipEntry) entries.nextElement();
239 final String className = entry.getName();
240 if (className.startsWith(packagePath)) {
241 if (className.endsWith(".class")
242 && !className.contains("$")) {
243 // The forward slash below is a forwards slash for
244 // both windows and linux
245
246 String class_forname = className.substring(0,className.length() - 6);
247 class_forname = class_forname.replace('/', '.');
248
249 classes.add(Class.forName(class_forname));
250 }
251 }
252 }
253 try {
254 jarFile.close();
255 } catch (IOException e) {
256 System.err.println("Error attempting to close Jar file");
257 e.printStackTrace();
258 }
259 } catch (IOException e) {
260 System.err.println("Error Instantiating Jar File Object");
261 e.printStackTrace();
262 }
263 } else {
264
265 System.err.println( "A Error has occured when trying to use a Jar file to find actions or agents."
266 + System.getProperty("line.separator")
267 + "Are you running from within Eclipse? (or just not with Jar) Then make sure your"
268 + " 'eclipse.expeditee.home' property is set correctly. It is currently: '"
269 + System.getProperty("eclipse.expeditee.home") + "'" + System.getProperty("line.separator")
270 + "You can set it by adding a VM argument. "
271 + "Example: -Declipse.expeditee.home=D:\\Desktop\\Research\\expeditee-svn");
272 }
273 } else {
274 String eclipse_expeditee_home = System.getProperty("eclipse.expeditee.home", "");
275 String full_package_path = eclipse_expeditee_home
276 + File.separator + "bin" + File.separator + "src" + File.separator + packagePath;
277
278 final File directory = new File(full_package_path);
279
280 if (directory.exists()) {
281 // Get the list of the files contained in the package
282 String[] files = directory.list();
283 for (int i = 0; i < files.length; i++) {
284 // we are only interested in .class files
285 if (files[i].endsWith(".class") && !files[i].contains("$")
286 && !files[i].equals("Actions.class")) {
287 // removes the .class extension
288 classes.add(Class.forName(pckgname
289 + files[i].substring(0, files[i].length() - 6)));
290 }
291 }
292 } else {
293 throw new ClassNotFoundException("The package '" + pckgname
294 + "' in the directory '" + directory
295 + "' does not appear to be a valid package");
296 }
297 }
298 Class<?>[] classesA = new Class<?>[classes.size()];
299 classes.toArray(classesA);
300 return classesA;
301 }
302
303 // public static Class[] getClasses(String pckgname)
304 // throws ClassNotFoundException {
305 // ArrayList<Class> classes = new ArrayList<Class>();
306 // // Get a File object for the package
307 // File directory = null;
308 // // Must be a forward slash for loading resources
309 // String path = pckgname.replace('.', '/');
310 // System.err.println("Get classes: " + path);
311 // try {
312 // ClassLoader cld = Thread.currentThread().getContextClassLoader();
313 // if (cld == null) {
314 // throw new ClassNotFoundException("Can't get class loader.");
315 // }
316 // URL resource = null;
317 // try {
318 // Enumeration<URL> resources = cld.getResources(path);
319 // System.err.println(resources);
320 // while (resources.hasMoreElements()) {
321 // URL url = resources.nextElement();
322 // // Ingore the classes in the test folder when we are running
323 // // the program from Eclipse
324 // // This doesnt apply when running directly from the jar
325 // // because the test classes are not compiled into the jar.
326 // // TODO change this so it is only done when running from
327 // // Eclipse... if it causes problems again!!
328 // // if (!url.toString().toLowerCase().contains("/tests/")) {
329 // resource = url;
330 // // break;
331 // // }
332 // }
333 // } catch (Exception e) {
334 // e.printStackTrace();
335 // }
336 // if (resource == null) {
337 // throw new ClassNotFoundException("No resource for " + path);
338 // }
339 // directory = new File(resource.getFile());
340 // } catch (NullPointerException x) {
341 // x.printStackTrace();
342 // throw new ClassNotFoundException(pckgname + " (" + directory
343 // + ") does not appear to be a valid package");
344 // }
345 // // System.out.println("Path:" + directory.getPath());
346 // int splitPoint = directory.getPath().indexOf('!');
347 // if (splitPoint > 0) {
348 // try {
349 // String jarName = directory.getPath().substring(
350 // "file:".length(), splitPoint);
351 // // Windows HACK
352 // if (jarName.indexOf(":") >= 0)
353 // jarName = jarName.substring(1);
354 //
355 // if (jarName.indexOf("%20") > 0) {
356 // jarName = jarName.replace("%20", " ");
357 // }
358 // // System.out.println("JarName:" + jarName);
359 // JarFile jarFile = new JarFile(jarName);
360 //
361 // Enumeration entries = jarFile.entries();
362 // int classCount = 0;
363 // while (entries.hasMoreElements()) {
364 // ZipEntry entry = (ZipEntry) entries.nextElement();
365 // String className = entry.getName();
366 // if (className.startsWith(path)) {
367 // if (className.endsWith(".class")
368 // && !className.contains("$")) {
369 // classCount++;
370 // // The forward slash below is a forwards slash for
371 // // both windows and linux
372 // classes.add(Class.forName(className.substring(0,
373 // className.length() - 6).replace('/', '.')));
374 // }
375 // }
376 // }
377 // jarFile.close();
378 // // System.out.println("Loaded " + classCount + " classes from "
379 // // + pckgname);
380 //
381 // } catch (Exception e) {
382 // e.printStackTrace();
383 // }
384 //
385 // } else {
386 //
387 // if (directory.exists()) {
388 // // Get the list of the files contained in the package
389 // String[] files = directory.list();
390 // for (int i = 0; i < files.length; i++) {
391 // // we are only interested in .class files
392 // if (files[i].endsWith(".class") && !files[i].contains("$")
393 // && !files[i].equals("Actions.class")) {
394 // // removes the .class extension
395 // classes
396 // .add(Class.forName(pckgname
397 // + files[i].substring(0, files[i]
398 // .length() - 6)));
399 // }
400 // }
401 // } else {
402 // throw new ClassNotFoundException("The package '" + pckgname +
403 // "' in the directory '" + directory
404 // + "' does not appear to be a valid package");
405 // }
406 // }
407 // Class[] classesA = new Class[classes.size()];
408 // classes.toArray(classesA);
409 // return classesA;
410 // }
411
412 /**
413 * Clears out the Action and JAG Hashtables and refills them. Normally this
414 * is only called once when the system starts.
415 *
416 * @return a warning message if there were any problems loading agents or
417 * actions.
418 */
419 public static Collection<String> Init() {
420
421 Collection<String> warnings = new LinkedList<String>();
422 Class<?>[] classes;
423
424 try {
425 classes = getClassesNew(AGENTS_PACKAGE);
426
427 for (int i = 0; i < classes.length; i++) {
428 String name = classes[i].getSimpleName();
429 // maps lower case name to correct capitalised name
430 _JAGs.put(name.toLowerCase(), classes[i].getName());
431 }
432
433 classes = getClassesNew(WIDGET_PACKAGE);
434
435 for (int i = 0; i < classes.length; i++) {
436 String name = classes[i].getSimpleName();
437 // maps lower case name to correct capitalised name
438 _IWs.put(name.toLowerCase(), WIDGET_PACKAGE + name);
439 }
440
441 classes = getClassesNew(CHARTS_PACKAGE);
442
443 for (int i = 0; i < classes.length; i++) {
444 String name = classes[i].getSimpleName();
445 // maps lower case name to correct capitalised name
446 _IWs.put("charts." + name.toLowerCase(), CHARTS_PACKAGE + name);
447 }
448 } catch (ClassNotFoundException e) {
449 System.err.println("ClassNotFoundException");
450 e.printStackTrace();
451 } catch (Exception e) {
452 warnings.add("You must have Java 1.5 or higher to run Expeditee");
453 warnings.add(e.getMessage());
454 e.printStackTrace();
455 }
456
457 try {
458 classes = getClassesNew(ACTIONS_PACKAGE);
459
460 for (int i = 0; i < classes.length; i++) {
461 String name = classes[i].getSimpleName();
462 // Ignore the test classes
463 if (name.toLowerCase().contains("test"))
464 continue;
465 // read in all the methods from the class
466 try {
467 // System.out.println(name)
468 LoadMethods(Class.forName(ACTIONS_PACKAGE + name));
469 } catch (ClassNotFoundException e) {
470 Logger.Log(e);
471 e.printStackTrace();
472 }
473 }
474 } catch (Exception e) {
475 warnings.add(e.getMessage());
476 }
477 return warnings;
478 }
479
480 /**
481 * Temporary, if a plugin system is devised then this would porbably become
482 * redundant. For now this allows external agents to be included.
483 *
484 * @param fullClassNames
485 * A set of full class names, that is, the class package and
486 * name. For example" "org.myplugin.agents.SerializedSearch"
487 *
488 * @return A collection of classes their were omitted because either there
489 * was a name clash with existing agents or did not exist. i.e. is
490 * completely successful this will be empty. Never null.
491 *
492 * @throws NullPointerException
493 * If fullClassNames is null.
494 *
495 */
496 public static Collection<String> addAgents(Set<String> fullClassNames) {
497 if (fullClassNames == null)
498 throw new NullPointerException("fullClassNames");
499
500 List<String> omittedAgents = new LinkedList<String>();
501
502 for (String fullName : fullClassNames) {
503
504 if (fullName == null || fullName.length() == 0)
505 continue;
506
507 boolean didAdd = false;
508
509 try {
510 // Does the class even exist?
511 Class<?> c = Class.forName(fullName);
512
513 String name = c.getSimpleName().toLowerCase();
514
515 if (!_JAGs.containsKey(name)) {
516
517 _JAGs.put(name, fullName);
518 didAdd = true;
519
520 }
521
522 } catch (ClassNotFoundException e) { // Nope it does not exist
523 e.printStackTrace();
524 }
525
526 if (!didAdd)
527 omittedAgents.add(fullName);
528
529 }
530
531 return omittedAgents;
532 }
533
534 /**
535 * Loads all the Methods that meet the requirements checked by MethodCheck
536 * into the hashtable.
537 *
538 * @param c
539 * The Class to load the Methods from.
540 */
541 public static void LoadMethods(Class<?> c) {
542 assert (c != null);
543
544 // list of methods to test
545 Method[] toLoad = c.getMethods();
546
547 for (Method m : toLoad) {
548 // only allow methods with the right modifiers
549 if (MethodCheck(m)) {
550 String lowercaseName = m.getName().toLowerCase();
551 if (!(_Actions.containsKey(lowercaseName)))
552 _Actions.put(lowercaseName, m);
553 else {
554 int i = 0;
555 while (_Actions.containsKey(lowercaseName + i))
556 i++;
557
558 _Actions.put(lowercaseName + i, m);
559 }
560
561 }
562 }
563 }
564
565 /**
566 * Checks if the given Method corresponds to the restrictions of Action
567 * commands, namely: Declared (not inherited), Public, and Static, with a
568 * void return type.
569 *
570 * @param m
571 * The Method to check
572 * @return True if the Method meets the above conditions, false otherwise.
573 */
574 private static boolean MethodCheck(Method m) {
575 int mods = m.getModifiers();
576
577 // check the method is declared (not inherited)
578 if ((mods & Method.DECLARED) != Method.DECLARED)
579 return false;
580
581 // check the method is public
582 if ((mods & Modifier.PUBLIC) != Modifier.PUBLIC)
583 return false;
584
585 // check the method is static
586 if ((mods & Modifier.STATIC) != Modifier.STATIC)
587 return false;
588
589 // if we have not returned yet, then the tests have all passed
590 return true;
591 }
592
593 /**
594 * Performs the given action command. The source Frame and Item are given
595 * because they are required by some actions. Note that the source frame
596 * does not have to be the Item's parent Frame.
597 *
598 * @param source
599 * The Frame that the action should apply to
600 * @param launcher
601 * The Item that has the action assigned to it
602 * @param command
603 * The action to perform
604 */
605 public static Object PerformAction(Frame source, Item launcher,
606 String command) throws Exception {
607 // if (!command.equalsIgnoreCase("Restore"))
608 // FrameIO.SaveFrame(source, false);
609 // TODO make restore UNDO the changes made by the last action
610
611 // separate method name and parameter names
612 String mname = getName(command);
613 command = command.substring(mname.length()).trim();
614 // If no params are provided get them from a text item on the cursor
615 if (command.length() == 0 && launcher instanceof Text
616 && launcher.isFloating()) {
617 command = launcher.getText();
618 }
619
620 // Strip off the @ from annotation items
621 if (mname.startsWith("@"))
622 mname = mname.substring(1);
623
624 mname = mname.trim();
625 String lowercaseName = mname.toLowerCase();
626 // check for protection on frame
627 if (ItemUtils.ContainsTag(source.getItems(), "@No" + mname)) {
628 throw new RuntimeException("Frame is protected by @No" + mname
629 + " tag.");
630 }
631
632 // retrieve methods that match the name
633 Method toRun = _Actions.get(lowercaseName);
634
635 // if this is not the name of a method, it may be the name of an agent
636 if (toRun == null) {
637 LaunchAgent(mname, command, source, launcher);
638 return null;
639 }
640
641 // Need to save the frame if we are navigating away from it so we dont
642 // loose changes
643 if (toRun.getDeclaringClass().getName().equals(NAVIGATIONS_CLASS)) {
644 FrameIO.SaveFrame(DisplayIO.getCurrentFrame());
645 }
646
647 // if there are duplicate methods with the same name
648 List<Method> possibles = new LinkedList<Method>();
649 possibles.add(toRun);
650 int i = 0;
651 while (_Actions.containsKey(lowercaseName + i)) {
652 possibles.add(_Actions.get(lowercaseName + i));
653 i++;
654 }
655
656 for (Method possible : possibles) {
657 // try first with the launching item as a parameter
658
659 // run method
660 try {
661 // convert parameters to objects and get the method to invoke
662 Object[] parameters = CreateObjects(possible, source, launcher,
663 command);
664 // Check that there are the same amount of params
665 if (parameters == null) {
666 continue;
667 }
668
669 return possible.invoke(null, parameters);
670 } catch (Exception e) {
671 Logger.Log(e);
672 e.printStackTrace();
673 }
674 }
675 // If the actions was not found... then it is run as an agent
676 assert (possibles.size() > 0);
677 throw new RuntimeException("Incorrect parameters for " + mname);
678 }
679
680 /**
681 * Launches an agent with the given name, and passes in the given parameters
682 *
683 * @param name
684 * The name of the JAG to load
685 * @param parameters
686 * The parameters to pass to the JAG
687 * @param source
688 * The starting Frame that the JAG is being launched on
689 */
690 private static void LaunchAgent(String name, String parameters,
691 Frame source, Item clicked) throws Exception {
692 // Use the correct case version for printing error messages
693 String nameWithCorrectCase = name;
694 name = name.toLowerCase();
695
696 String fullClassName = AGENTS_PACKAGE + name;
697
698 try {
699 // check for stored capitalisation
700 if (_JAGs.containsKey(name)) {
701 fullClassName = _JAGs.get(name);
702 } else if (name.endsWith("tree")) {
703 parameters = name.substring(0, name.length() - "tree".length())
704 + " " + parameters;
705 fullClassName = AGENTS_PACKAGE + "writetree";
706
707 } else if (name.endsWith("frame")) {
708 parameters = name
709 .substring(0, name.length() - "frame".length())
710 + " "
711 + parameters;
712 fullClassName = AGENTS_PACKAGE + "writeframe";
713 }
714
715 // load the JAG class
716 Class<?> agentClass = Class.forName(fullClassName);
717
718 // get the constructor for the JAG class
719 Constructor<?> con = null;
720 Constructor<?>[] constructors = agentClass.getConstructors();
721 Object[] params = null;
722
723 parameters = parameters.trim();
724 // determine correct parameters for constructor
725 for (Constructor<?> c : constructors) {
726 Class<?>[] paramTypes = c.getParameterTypes();
727 int paramCount = paramTypes.length;
728 if (paramCount > 0 && parameters.length() > 0) {
729 params = new Object[paramCount];
730 String[] paramStrings = parameters.split("\\s+");
731 /**
732 * Any extra parameters will be treated as the rest of the
733 * string if the last param is a string
734 */
735 if (paramCount > paramStrings.length) {
736 continue;
737 }
738
739 /**
740 * If there are extra parameters the last param must be a
741 * String
742 */
743 int lastParam = paramTypes.length - 1;
744
745 if (paramCount < paramStrings.length
746 && !paramTypes[lastParam].equals(String.class)) {
747 continue;
748 }
749
750 try {
751 for (int i = 0; i < paramCount; i++) {
752 SString nextParam = new SString(paramStrings[i]);
753 params[i] = null;
754 if (paramTypes[i].equals(int.class)
755 || paramTypes[i].equals(Integer.class)) {
756 params[i] = nextParam.integerValue().intValue();
757 } else if (paramTypes[i].equals(long.class)
758 || paramTypes[i].equals(Long.class)) {
759 params[i] = nextParam.integerValue();
760 } else if (paramTypes[i].equals(double.class)
761 || paramTypes[i].equals(Double.class)) {
762 params[i] = nextParam.doubleValue();
763 } else if (paramTypes[i].equals(float.class)
764 || paramTypes[i].equals(Float.class)) {
765 params[i] = nextParam.doubleValue()
766 .floatValue();
767 } else if (paramTypes[i].equals(boolean.class)
768 || paramTypes[i].equals(Boolean.class)) {
769 params[i] = nextParam.booleanValue();
770 } else if (paramTypes[i].equals(String.class)) {
771 params[i] = nextParam.stringValue();
772 } else {
773 throw new UnexpectedException(
774 "Unexpected type "
775 + paramTypes[i].getClass()
776 .toString());
777 }
778 }
779 } catch (Exception e) {
780 continue;
781 }
782
783 if (paramCount < paramStrings.length) {
784
785 /**
786 * Append extra params on the end of the last string
787 * param
788 */
789 String s = params[lastParam].toString();
790 for (int i = paramCount; i < paramStrings.length; i++) {
791 s += ' ' + paramStrings[i];
792 }
793 params[lastParam] = s;
794 }
795
796 con = c;
797 break;
798 } else if (c.getParameterTypes().length == 0 && con == null) {
799 con = c;
800 params = null;
801 }
802 }
803
804 // if there is no constructor, return
805 if (con == null) {
806 throw new RuntimeException(INVALID_PARAMETERS_ERROR
807 + nameWithCorrectCase);
808 }
809
810 // create the JAG
811 Agent toLaunch = (Agent) con.newInstance(params);
812
813 LaunchAgent(toLaunch, source, clicked);
814
815 } catch (ClassNotFoundException cnf) {
816 _Agent = null;
817 throw new RuntimeException("'" + nameWithCorrectCase
818 + "' is not an action or agent.");
819 }
820 }
821
822 public static void LaunchAgent(String name, String parameters, Frame source)
823 throws Exception {
824 LaunchAgent(name, parameters, source, null);
825 }
826
827 /**
828 * Launches an agent from an already instantiated object.
829 *
830 * @param agent
831 * The agent to launch. Must not be null.
832 *
833 * @param source
834 * The calling frame that launched it. Must not be null.
835 *
836 * @param itemParam
837 * The item parameter for the agent.
838 *
839 * @throws NullPointerException
840 * if any of the arguments are null.
841 */
842 public static void LaunchAgent(Agent agent, Frame source, Item itemParam) {
843
844 if (agent == null)
845 throw new NullPointerException("agent");
846 if (source == null)
847 throw new NullPointerException("source");
848 // if (itemParam == null) throw new NullPointerException("itemParam");
849
850 String nameWithCorrectCase = agent.getClass().getSimpleName();
851
852 try {
853
854 // create the JAG
855 _Agent = agent;
856
857 Thread t = new Thread(_Agent);
858 t.setPriority(Thread.MIN_PRIORITY);
859
860 if (FreeItems.textOnlyAttachedToCursor()) {
861 itemParam = FreeItems.getItemAttachedToCursor();
862 }
863
864 // check for errors during initialisation
865 if (!_Agent.initialise(source, itemParam)) {
866 _Agent = null;
867 throw new RuntimeException("Error initialising agent: "
868 + nameWithCorrectCase);
869 }
870
871 // save the current frame (if necesssary)
872 // TODO make this nicer... ie. make Format an action rather than an
873 // agent and save frames only before running agents
874 if (!nameWithCorrectCase.equalsIgnoreCase("format")
875 && !nameWithCorrectCase.equalsIgnoreCase("sort")) {
876 FrameUtils.LeavingFrame(source);
877 }
878
879 if (_Agent.hasResultString()) {
880 // Just run the agent on this thread... dont run it in the
881 // background
882 t.run();
883 String result = _Agent.toString();
884 // Attach the result to the cursor
885 if (FreeItems.textOnlyAttachedToCursor()) {
886 Item resultItem = FreeItems.getItemAttachedToCursor();
887 resultItem.setText(result);
888 }
889 // if there is a completion frame, then display it to the user
890 } else {
891 t.start();
892 if (_Agent.hasResultFrame()) {
893 // TODO We want to be able to navigate through the frames as
894 // the results are loading
895 Frame next = _Agent.getResultFrame();
896 FrameUtils.DisplayFrame(next, true, true);
897 }
898 }
899 } catch (Exception e) {
900 _Agent = null;
901 e.printStackTrace();
902 throw new RuntimeException("Error creating Agent: '"
903 + nameWithCorrectCase + "'");
904 }
905 FrameGraphics.refresh(false);
906 }
907
908 /**
909 * Used to determine if the previously launched agent is still executing.
910 *
911 * @return True if the last Agent is still executing, False otherwise.
912 */
913 public static boolean isAgentRunning() {
914 if (_Agent != null)
915 return _Agent.isRunning();
916
917 return false;
918 }
919
920 /**
921 * Stops the currently running Agent (If there is one) by calling
922 * Agent.stop(). Note: This may not stop the Agent immediately, but the
923 * Agent should terminate as soon as it is safe to do so.
924 */
925 public static void stopAgent() {
926 if (_Agent != null && _Agent.isRunning()) {
927 MessageBay.errorMessage("Stopping Agent...");
928 _Agent.stop();
929 }
930 }
931
932 public static void interruptAgent() {
933 if (_Agent != null) {
934 _Agent.interrupt();
935 }
936 }
937
938 /**
939 * Converts the given String of values into an array of Objects
940 *
941 * @param launcher
942 * The Item used to launch the action, it may be required as a
943 * parameter
944 * @param values
945 * A list of space separated String values to convert to objects
946 * @return The created array of Objects
947 */
948 public static Object[] CreateObjects(Method method, Frame source,
949 Item launcher, String values) {
950 // The parameter types that should be created from the given String
951 Class<?>[] paramTypes = method.getParameterTypes();
952
953 int paramCount = paramTypes.length;
954 // if the method has no parameters
955 if (paramCount == 0)
956 return new Object[0];
957
958 Object[] objects = new Object[paramCount];
959 int ind = 0;
960
961 /*
962 * if the first class in the list is a frame or item, it is the source
963 * or launcher length must be at least one if we are still running
964 */
965 if (paramTypes[ind] == Frame.class) {
966 objects[ind] = source;
967 ind++;
968 }
969
970 // Check if the second item is an item
971 if (paramCount > ind && Item.class.isAssignableFrom(paramTypes[ind])) {
972 objects[ind] = launcher;
973 ind++;
974 }// If there is stuff on the cursor use it for the rest of the params
975 else if (launcher != null && launcher.isFloating()) {
976 values = launcher.getText();
977 }
978
979 String param = values;
980 // convert the rest of the objects
981 for (; ind < objects.length; ind++) {
982 // check if its the last param and combine
983 if (values.length() > 0 && ind == objects.length - 1) {
984 param = values.trim();
985 // check if its a string
986 if (param.length() > 0 && param.charAt(0) == '"') {
987 int endOfString = param.indexOf('"', 1);
988 if (endOfString > 0) {
989 param = param.substring(1, endOfString);
990 }
991 }
992 } else {// strip off the next value
993 param = ParseValue(values);
994 values = RemainingParams(values);
995 }
996 // convert the value to an object
997 try {
998 Object o = Conversion.Convert(paramTypes[ind], param);
999 if (o == null)
1000 return null;
1001 objects[ind] = o;
1002 } catch (Exception e) {
1003 return null;
1004 }
1005 }
1006
1007 return objects;
1008 }
1009
1010 /**
1011 * Returns a string containing the remaining params after ignoring the first
1012 * one.
1013 *
1014 * @param params
1015 * a space sparated list of N parameters
1016 * @return the remaining N - 1 parameters
1017 */
1018 public static String RemainingParams(String params) {
1019 if (params.length() == 0)
1020 return null;
1021
1022 // remove leading and trailing spaces
1023 params = params.trim();
1024
1025 // if there are no more parameters, we are done
1026 if (params.indexOf(" ") < 0) {
1027 return "";
1028 }
1029
1030 // Check if we have a string parameter
1031 if (params.charAt(0) == '"') {
1032 int endOfString = params.indexOf('"', 1);
1033 if (endOfString > 0) {
1034 if (endOfString > params.length())
1035 return "";
1036 return params.substring(endOfString + 1).trim();
1037 }
1038 }
1039
1040 return params.substring(params.indexOf(" ")).trim();
1041 }
1042
1043 /**
1044 * Returns the first value in the space separated String of parameters
1045 * passed in. Strings are enclosed in double quotes.
1046 *
1047 * @param params
1048 * The String of space separated values
1049 * @return The first value in the String
1050 */
1051 public static String ParseValue(String params) {
1052 if (params.length() == 0)
1053 return null;
1054
1055 // remove leading and trailing spaces
1056 String param = params.trim();
1057
1058 // Check if we have a string parameter
1059 if (param.charAt(0) == '"') {
1060 int endOfString = param.indexOf('"', 1);
1061 if (endOfString > 0)
1062 return param.substring(1, endOfString);
1063 }
1064
1065 // if there are no more parameters, we are done
1066 if (param.indexOf(" ") < 0) {
1067 return param;
1068 }
1069
1070 return param.substring(0, param.indexOf(" "));
1071 }
1072
1073 /**
1074 * Separates the name of the given command from any parameters and returns
1075 * them
1076 *
1077 * @param command
1078 * The String to separate out the Action or Agent name from
1079 * @return The name of the Action of Agent with parameters stripped off
1080 */
1081 private static String getName(String command) {
1082 if (command.indexOf(" ") < 0)
1083 return command;
1084
1085 return command.substring(0, command.indexOf(" "));
1086 }
1087
1088 /**
1089 * Gets an uncapitalized font name and returns the capitalized font name.
1090 * The capitalized form can be used with the Font.decoded method to get a
1091 * corresponding Font object.
1092 *
1093 * @param fontName
1094 * a font name in mixed case
1095 * @return the correct capitalized form of the font name
1096 */
1097 public static String getCapitalizedFontName(String fontName) {
1098 // Initialize the fonts if they have not already been loaded
1099 initFonts();
1100 return _Fonts.get(fontName.toLowerCase());
1101 }
1102
1103 /**
1104 * Initialise the fontsList if it has not been done already
1105 */
1106 private static void initFonts() {
1107 if (_Fonts.size() == 0) {
1108 String[] availableFonts = GraphicsEnvironment
1109 .getLocalGraphicsEnvironment()
1110 .getAvailableFontFamilyNames();
1111 for (String s : availableFonts) {
1112 _Fonts.put(s.toLowerCase(), s);
1113 }
1114 }
1115 }
1116
1117 public static HashMap<String, String> getFonts() {
1118 initFonts();
1119 return _Fonts;
1120 }
1121
1122 public static Object PerformActionCatchErrors(Frame current, Item launcher,
1123 String command) {
1124 try {
1125 return PerformAction(current, launcher, command);
1126 } catch (RuntimeException e) {
1127 e.printStackTrace();
1128 MessageBay.errorMessage("Action failed: " + e.getMessage());
1129 } catch (Exception e) {
1130 e.printStackTrace();
1131 MessageBay.errorMessage("Action failed: "
1132 + e.getClass().getSimpleName());
1133 }
1134 return null;
1135 }
1136
1137 /**
1138 * Gets the full class path for a widget with a given case insensitive name.
1139 *
1140 * @param widgetName
1141 * @return
1142 */
1143 public static String getClassName(String widgetName) {
1144 return _IWs.get(widgetName.toLowerCase());
1145 }
1146
1147 static List<String> getActions() {
1148 List<String> actionNames = new LinkedList<String>();
1149 for (Method m : _Actions.values()) {
1150 StringBuilder sb = new StringBuilder();
1151 sb.append(m.getName());
1152 for (Class<?> c : m.getParameterTypes()) {
1153 sb.append(" ").append(c.getSimpleName());
1154 }
1155 actionNames.add(sb.toString());
1156 }
1157
1158 return actionNames;
1159 }
1160
1161 static List<String> getAgents() {
1162 List<String> agentNames = new LinkedList<String>();
1163
1164 for (String s : _JAGs.values()) {
1165 agentNames.add(s.substring(s.lastIndexOf('.') + 1));
1166 }
1167
1168 return agentNames;
1169 }
1170}
Note: See TracBrowser for help on using the repository browser.