Ignore:
Timestamp:
07/10/13 12:05:45 (11 years ago)
Author:
bln4
Message:

Modifications are to Actions.getClasses. Some people were having issues with this loading actions and agents when running through eclipse. The code now respects two states; either the user has set a 'expeditee.home' variable or not. If running through eclipse then it needs to be set (running through the Jar should respect the variable if present but doesn't need it). The exception messages are now more detailed as too what has likely gone wrong; this includes mentioning the possibility of needing to set this variable and gives an example of how to do that.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/expeditee/actions/Actions.java

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