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