/** * Actions.java * Copyright (C) 2010 New Zealand Digital Library, http://expeditee.org * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.expeditee.actions; import java.awt.GraphicsEnvironment; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.rmi.UnexpectedException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.expeditee.agents.Agent; import org.expeditee.gui.DisplayIO; import org.expeditee.gui.Frame; import org.expeditee.gui.FrameGraphics; import org.expeditee.gui.FrameIO; import org.expeditee.gui.FrameUtils; import org.expeditee.gui.FreeItems; import org.expeditee.gui.MessageBay; import org.expeditee.io.Conversion; import org.expeditee.items.Item; import org.expeditee.items.ItemUtils; import org.expeditee.items.Text; import org.expeditee.reflection.PackageLoader; import org.expeditee.simple.SString; import org.expeditee.stats.Logger; /** * The Action class is used to launch Actions and Agents. * * This class checks all class files in the same directory, and reads in and * adds all the methods from them. The methods are stored in a Hashtable so that * the lowercase method names can be mapped to the correctly capatilized method * names (to provide case-insensitivity) * * When adding an action to a class in the actions folder the following must be * considered:
  • If the first parameter is of type Frame, the current frame * will be passed as a parameter.
  • If the next param is of type Item the item * on the end of the cursor will be passed or the item that was clicked to * execute the action if nothing is on the end of the cursor. current frame or * item.
  • If there are multiple overloads for the same method they * should be declared in order of the methods with the most parameteres to least * parameters.
  • */ public class Actions { private static final String INVALID_PARAMETERS_ERROR = "Invalid parameters for agent: "; //$NON-NLS-1$ // the currently running agent (if there is one) private static Agent _Agent = null; // maps lower case method names to the method private static HashMap _Actions = new HashMap(); // map lower case fonts to capitalized fonts private static HashMap _Fonts = new HashMap(); // maps lower case JAG class names to capitalized JAG full class names private static HashMap _JAGs = new HashMap(); // maps lower case IW class names to capitalized IW names private static HashMap _IWs = new HashMap(); public static final String ROOT_PACKAGE = "org.expeditee."; // Package and class file locations public static final String ACTIONS_PACKAGE = ROOT_PACKAGE + "actions."; public static final String AGENTS_PACKAGE = ROOT_PACKAGE + "agents."; public static final String WIDGET_PACKAGE = ROOT_PACKAGE + "items.widgets."; public static final String CHARTS_PACKAGE = ROOT_PACKAGE + "items.widgets.charts."; public static final String NAVIGATIONS_CLASS = ROOT_PACKAGE + "actions.NavigationActions"; // public static Class[] getClasses(String pckgname) // throws ClassNotFoundException { // ArrayList classes = new ArrayList(); // // Get a File object for the package // File directory = null; // // Must be a forward slash for loading resources // String path = pckgname.replace('.', '/'); // System.err.println("Get classes: " + path); // try { // ClassLoader cld = Thread.currentThread().getContextClassLoader(); // if (cld == null) { // throw new ClassNotFoundException("Can't get class loader."); // } // URL resource = null; // try { // Enumeration resources = cld.getResources(path); // System.err.println(resources); // while (resources.hasMoreElements()) { // URL url = resources.nextElement(); // // Ingore the classes in the test folder when we are running // // the program from Eclipse // // This doesnt apply when running directly from the jar // // because the test classes are not compiled into the jar. // // TODO change this so it is only done when running from // // Eclipse... if it causes problems again!! // // if (!url.toString().toLowerCase().contains("/tests/")) { // resource = url; // // break; // // } // } // } catch (Exception e) { // e.printStackTrace(); // } // if (resource == null) { // throw new ClassNotFoundException("No resource for " + path); // } // directory = new File(resource.getFile()); // } catch (NullPointerException x) { // x.printStackTrace(); // throw new ClassNotFoundException(pckgname + " (" + directory // + ") does not appear to be a valid package"); // } // // System.out.println("Path:" + directory.getPath()); // int splitPoint = directory.getPath().indexOf('!'); // if (splitPoint > 0) { // try { // String jarName = directory.getPath().substring( // "file:".length(), splitPoint); // // Windows HACK // if (jarName.indexOf(":") >= 0) // jarName = jarName.substring(1); // // if (jarName.indexOf("%20") > 0) { // jarName = jarName.replace("%20", " "); // } // // System.out.println("JarName:" + jarName); // JarFile jarFile = new JarFile(jarName); // // Enumeration entries = jarFile.entries(); // int classCount = 0; // while (entries.hasMoreElements()) { // ZipEntry entry = (ZipEntry) entries.nextElement(); // String className = entry.getName(); // if (className.startsWith(path)) { // if (className.endsWith(".class") // && !className.contains("$")) { // classCount++; // // The forward slash below is a forwards slash for // // both windows and linux // classes.add(Class.forName(className.substring(0, // className.length() - 6).replace('/', '.'))); // } // } // } // jarFile.close(); // // System.out.println("Loaded " + classCount + " classes from " // // + pckgname); // // } catch (Exception e) { // e.printStackTrace(); // } // // } else { // // if (directory.exists()) { // // Get the list of the files contained in the package // String[] files = directory.list(); // for (int i = 0; i < files.length; i++) { // // we are only interested in .class files // if (files[i].endsWith(".class") && !files[i].contains("$") // && !files[i].equals("Actions.class")) { // // removes the .class extension // classes // .add(Class.forName(pckgname // + files[i].substring(0, files[i] // .length() - 6))); // } // } // } else { // throw new ClassNotFoundException("The package '" + pckgname + // "' in the directory '" + directory // + "' does not appear to be a valid package"); // } // } // Class[] classesA = new Class[classes.size()]; // classes.toArray(classesA); // return classesA; // } /** * Clears out the Action and JAG Hashtables and refills them. Normally this * is only called once when the system starts. * * @return a warning message if there were any problems loading agents or * actions. */ public static Collection Init() { Collection warnings = new LinkedList(); List> classes; try { classes = PackageLoader.getClassesNew(AGENTS_PACKAGE); for (Class clazz : classes) { String name = clazz.getSimpleName(); // maps lower case name to correct capitalised name _JAGs.put(name.toLowerCase(), clazz.getName()); } classes = PackageLoader.getClassesNew(WIDGET_PACKAGE); for (Class clazz : classes) { String name = clazz.getSimpleName(); // maps lower case name to correct capitalised name _IWs.put(name.toLowerCase(), WIDGET_PACKAGE + name); } classes = PackageLoader.getClassesNew(CHARTS_PACKAGE); for (Class clazz : classes) { String name = clazz.getSimpleName(); // maps lower case name to correct capitalised name _IWs.put("charts." + name.toLowerCase(), CHARTS_PACKAGE + name); } } catch (ClassNotFoundException e) { System.err.println("ClassNotFoundException"); e.printStackTrace(); } catch (Exception e) { warnings.add("You must have Java 1.5 or higher to run Expeditee"); warnings.add(e.getMessage()); e.printStackTrace(); } try { classes = PackageLoader.getClassesNew(ACTIONS_PACKAGE); for (Class clazz : classes) { String name = clazz.getSimpleName(); // Ignore the test classes if (name.toLowerCase().contains("test")) continue; // read in all the methods from the class try { // System.out.println(name) LoadMethods(Class.forName(ACTIONS_PACKAGE + name)); } catch (ClassNotFoundException e) { Logger.Log(e); e.printStackTrace(); } } } catch (Exception e) { warnings.add(e.getMessage()); } return warnings; } /** * Temporary, if a plugin system is devised then this would porbably become * redundant. For now this allows external agents to be included. * * @param fullClassNames * A set of full class names, that is, the class package and * name. For example" "org.myplugin.agents.SerializedSearch" * * @return A collection of classes their were omitted because either there * was a name clash with existing agents or did not exist. i.e. is * completely successful this will be empty. Never null. * * @throws NullPointerException * If fullClassNames is null. * */ public static Collection addAgents(Set fullClassNames) { if (fullClassNames == null) throw new NullPointerException("fullClassNames"); List omittedAgents = new LinkedList(); for (String fullName : fullClassNames) { if (fullName == null || fullName.length() == 0) continue; boolean didAdd = false; try { // Does the class even exist? Class c = Class.forName(fullName); String name = c.getSimpleName().toLowerCase(); if (!_JAGs.containsKey(name)) { _JAGs.put(name, fullName); didAdd = true; } } catch (ClassNotFoundException e) { // Nope it does not exist e.printStackTrace(); } if (!didAdd) omittedAgents.add(fullName); } return omittedAgents; } /** * Loads all the Methods that meet the requirements checked by MethodCheck * into the hashtable. * * @param c * The Class to load the Methods from. */ public static void LoadMethods(Class c) { assert (c != null); // list of methods to test Method[] toLoad = c.getMethods(); for (Method m : toLoad) { // only allow methods with the right modifiers if (MethodCheck(m)) { String lowercaseName = m.getName().toLowerCase(); if (!(_Actions.containsKey(lowercaseName))) _Actions.put(lowercaseName, m); else { int i = 0; while (_Actions.containsKey(lowercaseName + i)) i++; _Actions.put(lowercaseName + i, m); } } } } /** * Checks if the given Method corresponds to the restrictions of Action * commands, namely: Declared (not inherited), Public, and Static, with a * void return type. * * @param m * The Method to check * @return True if the Method meets the above conditions, false otherwise. */ private static boolean MethodCheck(Method m) { int mods = m.getModifiers(); // check the method is declared (not inherited) if ((mods & Method.DECLARED) != Method.DECLARED) return false; // check the method is public if ((mods & Modifier.PUBLIC) != Modifier.PUBLIC) return false; // check the method is static if ((mods & Modifier.STATIC) != Modifier.STATIC) return false; // if we have not returned yet, then the tests have all passed return true; } /** * Performs the given action command. The source frame and item are given * because they are required by some actions. Note that the source frame * does not ahve to be the items parent frame. * * If multiple actions exist with the name command.takeWhile(_ != ' ') then * it attempts to find candidate that best matches the arguments passed in. * (arguments are command.dropWhile(_ != ' ') or is floating) * * @param source * The frame that the action should apply to * @param launcher * The item that has the action assigned to it * @param command * The action to perform * @return * @throws Exception */ public static Object PerformAction(final Frame source, final Item launcher, final String command) throws Exception { final String actionName = getName(command); final String parameters = command.substring(actionName.length()).trim(); // Check for protection on frame. if (ItemUtils.ContainsTag(source.getItems(), "@No" + actionName)) { final String errorMsg = "Frame is protected by @No" + actionName + " tag."; MessageBay.errorMessage(errorMsg); throw new RuntimeException(errorMsg); } // Find all canditates. Sort by quantity of arguments. final String lowercaseName = actionName.toLowerCase(); final Method firstCanditate = _Actions.get(lowercaseName); // if this is not the name of a method, it may be the name of an agent if (firstCanditate == null) { String commandAgent = command.substring(actionName.length()).trim(); if(commandAgent.length() == 0 && launcher instanceof Text && launcher.isFloating()) commandAgent = launcher.getText(); LaunchAgent(actionName, commandAgent, source, launcher); return null; } // need to save the frame if we are navigating away from it so we dont // loose changes if (firstCanditate.getDeclaringClass().getName().equals(NAVIGATIONS_CLASS)) { FrameIO.SaveFrame(DisplayIO.getCurrentFrame()); } final List canditates = new LinkedList(); canditates.add(firstCanditate); int i = 0; while (_Actions.containsKey(lowercaseName + i)) canditates.add(_Actions.get(lowercaseName + i++)); canditates.sort(new Comparator() { @Override public int compare(final Method m1, final Method m2) { final int parameterCountDifference = m2.getParameterCount() - m1.getParameterCount(); if(parameterCountDifference == 0) { final Class[] m1ParamTypes = m1.getParameterTypes(); final Class[] m2ParamTypes = m2.getParameterTypes(); //final ArrayList> typeOrder = Arrays.asList(Integer.TYPE, Double.TYPE, Float.TYPE, String.class); // The above line doesn't compile in Eclipse Mars with the compile set to v1.8 // So changed to the following (low-tech) way: ArrayList> typeOrder = new ArrayList>(); typeOrder.add(Integer.TYPE); typeOrder.add(Double.TYPE); typeOrder.add(Float.TYPE); typeOrder.add(String.class); for(int i = 0,o = 0; i < m1ParamTypes.length && o < m2ParamTypes.length; i++,o++) { final Class m1ParamType = m1ParamTypes[i]; final Class m2ParamType = m2ParamTypes[o]; final int m1ParamTypeIndex = typeOrder.indexOf(m1ParamType) != -1 ? typeOrder.indexOf(m1ParamType) : typeOrder.size(); final int m2ParamTypeIndex = typeOrder.indexOf(m2ParamType) != -1 ? typeOrder.indexOf(m2ParamType) : typeOrder.size(); final int paramMagnitude = m2ParamTypeIndex - m1ParamTypeIndex; if(paramMagnitude != 0) return paramMagnitude; } } return parameterCountDifference; } }); // Find best candidate. Start searching with the candidate with most // arguments. for (final Method canditate : canditates) { final Object[] paramObjects = CreateObjects(canditate, source, launcher, parameters); try { if (paramObjects != null) return canditate.invoke(null, paramObjects); } catch (final Exception e) { Logger.Log(e); e.printStackTrace(); } } assert (canditates.size() > 0); final String nl = System.getProperty("line.separator"); final StringBuilder errorBuilder = new StringBuilder( "Incorrect parameters for " + actionName + nl + "Canditates: "); for (final Method canditate : canditates) errorBuilder.append(canditate + nl); throw new RuntimeException(errorBuilder.toString()); } // /** // * Performs the given action command. The source Frame and Item are given // because they are required by some actions. // * Note that the source frame does not have to be the Item's parent Frame. // * // * @param source // * The Frame that the action should apply to // * @param launcher // * The Item that has the action assigned to it // * @param command // * The action to perform // */ // public static Object PerformAction(Frame source, Item launcher, String // command) throws Exception { // // if (!command.equalsIgnoreCase("Restore")) // // FrameIO.SaveFrame(source, false); // // TODO make restore UNDO the changes made by the last action // // // separate method name and parameter names // String mname = getName(command); // command = command.substring(mname.length()).trim(); // // If no params are provided get them from a text item on the cursor // if (command.length() == 0 && launcher instanceof Text && // launcher.isFloating()) { // command = launcher.getText(); // } // // // Strip off the @ from annotation items // if (mname.startsWith("@")) // mname = mname.substring(1); // // mname = mname.trim(); // String lowercaseName = mname.toLowerCase(); // // check for protection on frame // if (ItemUtils.ContainsTag(source.getItems(), "@No" + mname)) { // throw new RuntimeException("Frame is protected by @No" + mname + // " tag."); // } // // // retrieve methods that match the name // Method toRun = _Actions.get(lowercaseName); // // // if this is not the name of a method, it may be the name of an agent // if (toRun == null) { // LaunchAgent(mname, command, source, launcher); // return null; // } // // // Need to save the frame if we are navigating away from it so we dont // // loose changes // if (toRun.getDeclaringClass().getName().equals(NAVIGATIONS_CLASS)) { // FrameIO.SaveFrame(DisplayIO.getCurrentFrame()); // } // // // if there are duplicate methods with the same name // List possibles = new LinkedList(); // possibles.add(toRun); // int i = 0; // while (_Actions.containsKey(lowercaseName + i)) { // possibles.add(_Actions.get(lowercaseName + i)); // i++; // } // // for (Method possible : possibles) { // // try first with the launching item as a parameter // // // run method // try { // // convert parameters to objects and get the method to invoke // Object[] parameters = CreateObjects(possible, source, launcher, command); // // Check that there are the same amount of params // if (parameters == null) { // continue; // } // // return possible.invoke(null, parameters); // } catch (Exception e) { // Logger.Log(e); // e.printStackTrace(); // } // } // // If the actions was not found... then it is run as an agent // assert (possibles.size() > 0); // throw new RuntimeException("Incorrect parameters for " + mname); // } /** * Launches an agent with the given name, and passes in the given parameters * * @param name * The name of the JAG to load * @param parameters * The parameters to pass to the JAG * @param source * The starting Frame that the JAG is being launched on */ private static void LaunchAgent(String name, String parameters, Frame source, Item clicked) throws Exception { // Use the correct case version for printing error messages String nameWithCorrectCase = name; name = name.toLowerCase(); String fullClassName = AGENTS_PACKAGE + name; try { // check for stored capitalisation if (_JAGs.containsKey(name)) { fullClassName = _JAGs.get(name); } else if (name.endsWith("tree")) { parameters = name.substring(0, name.length() - "tree".length()) + " " + parameters; fullClassName = AGENTS_PACKAGE + "writetree"; } else if (name.endsWith("frame")) { parameters = name .substring(0, name.length() - "frame".length()) + " " + parameters; fullClassName = AGENTS_PACKAGE + "writeframe"; } // load the JAG class Class agentClass = Class.forName(fullClassName); // get the constructor for the JAG class Constructor con = null; Constructor[] constructors = agentClass.getConstructors(); Object[] params = null; parameters = parameters.trim(); // determine correct parameters for constructor for (Constructor c : constructors) { Class[] paramTypes = c.getParameterTypes(); int paramCount = paramTypes.length; if (paramCount > 0 && parameters.length() > 0) { params = new Object[paramCount]; String[] paramStrings = parameters.split("\\s+"); /** * Any extra parameters will be treated as the rest of the * string if the last param is a string */ if (paramCount > paramStrings.length) { continue; } /** * If there are extra parameters the last param must be a * String */ int lastParam = paramTypes.length - 1; if (paramCount < paramStrings.length && !paramTypes[lastParam].equals(String.class)) { continue; } try { for (int i = 0; i < paramCount; i++) { SString nextParam = new SString(paramStrings[i]); params[i] = null; if (paramTypes[i].equals(int.class) || paramTypes[i].equals(Integer.class)) { params[i] = nextParam.integerValue().intValue(); } else if (paramTypes[i].equals(long.class) || paramTypes[i].equals(Long.class)) { params[i] = nextParam.integerValue(); } else if (paramTypes[i].equals(double.class) || paramTypes[i].equals(Double.class)) { params[i] = nextParam.doubleValue(); } else if (paramTypes[i].equals(float.class) || paramTypes[i].equals(Float.class)) { params[i] = nextParam.doubleValue() .floatValue(); } else if (paramTypes[i].equals(boolean.class) || paramTypes[i].equals(Boolean.class)) { params[i] = nextParam.booleanValue(); } else if (paramTypes[i].equals(String.class)) { params[i] = nextParam.stringValue(); } else { throw new UnexpectedException( "Unexpected type " + paramTypes[i].getClass() .toString()); } } } catch (Exception e) { continue; } if (paramCount < paramStrings.length) { /** * Append extra params on the end of the last string * param */ String s = params[lastParam].toString(); for (int i = paramCount; i < paramStrings.length; i++) { s += ' ' + paramStrings[i]; } params[lastParam] = s; } con = c; break; } else if (c.getParameterTypes().length == 0 && con == null) { con = c; params = null; } } // if there is no constructor, return if (con == null) { throw new RuntimeException(INVALID_PARAMETERS_ERROR + nameWithCorrectCase); } // create the JAG Agent toLaunch = (Agent) con.newInstance(params); LaunchAgent(toLaunch, source, clicked); } catch (ClassNotFoundException cnf) { _Agent = null; throw new RuntimeException("'" + nameWithCorrectCase + "' is not an action or agent."); } } public static void LaunchAgent(String name, String parameters, Frame source) throws Exception { LaunchAgent(name, parameters, source, null); } /** * Launches an agent from an already instantiated object. * * @param agent * The agent to launch. Must not be null. * * @param source * The calling frame that launched it. Must not be null. * * @param itemParam * The item parameter for the agent. * * @throws NullPointerException * if any of the arguments are null. */ public static void LaunchAgent(Agent agent, Frame source, Item itemParam) { if (agent == null) throw new NullPointerException("agent"); if (source == null) throw new NullPointerException("source"); // if (itemParam == null) throw new NullPointerException("itemParam"); String nameWithCorrectCase = agent.getClass().getSimpleName(); try { // create the JAG _Agent = agent; Thread.UncaughtExceptionHandler h = new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread th, Throwable ex) { MessageBay.errorMessage("Error occurred in Action: " + th.getName()); ex.printStackTrace(); stopAgent(); _Agent = null; } }; Thread t = new Thread(_Agent, nameWithCorrectCase); t.setPriority(Thread.MIN_PRIORITY); t.setUncaughtExceptionHandler(h); if (FreeItems.textOnlyAttachedToCursor()) { itemParam = FreeItems.getItemAttachedToCursor(); } // check for errors during initialisation if (!_Agent.initialise(source, itemParam)) { _Agent = null; throw new RuntimeException("Error initialising agent: " + nameWithCorrectCase); } // save the current frame (if necesssary) // TODO make this nicer... ie. make Format an action rather than an // agent and save frames only before running agents if (!nameWithCorrectCase.equalsIgnoreCase("format") && !nameWithCorrectCase.equalsIgnoreCase("sort")) { FrameUtils.LeavingFrame(source); } if (_Agent.hasResultString()) { // Just run the agent on this thread... dont run it in the // background t.run(); if (_Agent != null) { String result = _Agent.toString(); // Attach the result to the cursor if (FreeItems.textOnlyAttachedToCursor()) { Item resultItem = FreeItems.getItemAttachedToCursor(); resultItem.setText(result); } // if there is a completion frame, then display it to the // user } } else { t.start(); if (_Agent != null && _Agent.hasResultFrame()) { // TODO We want to be able to navigate through the frames as // the results are loading Frame next = _Agent.getResultFrame(); FrameUtils.DisplayFrame(next, true, true); } } } catch (Exception e) { _Agent = null; e.printStackTrace(); throw new RuntimeException("Error creating Agent: '" + nameWithCorrectCase + "'"); } FrameGraphics.refresh(false); } /** * Used to determine if the previously launched agent is still executing. * * @return True if the last Agent is still executing, False otherwise. */ public static boolean isAgentRunning() { if (_Agent != null) return _Agent.isRunning(); return false; } /** * Stops the currently running Agent (If there is one) by calling * Agent.stop(). Note: This may not stop the Agent immediately, but the * Agent should terminate as soon as it is safe to do so. */ public static void stopAgent() { if (_Agent != null && _Agent.isRunning()) { MessageBay.errorMessage("Stopping Agent..."); _Agent.stop(); } } public static void interruptAgent() { if (_Agent != null) { _Agent.interrupt(); } } /** * Converts the given String of values into an array of Objects * * @param launcher * The Item used to launch the action, it may be required as a * parameter * @param values * A list of space separated String values to convert to objects * @return The created array of Objects */ public static Object[] CreateObjects(Method method, Frame source, Item launcher, String values) { // The parameter types that should be created from the given String Class[] paramTypes = method.getParameterTypes(); int paramCount = paramTypes.length; // if the method has no parameters if (paramCount == 0) return new Object[0]; Object[] objects = new Object[paramCount]; int ind = 0; /* * if the first class in the list is a frame or item, it is the source * or launcher length must be at least one if we are still running */ if (paramTypes[ind] == Frame.class) { objects[ind] = source; ind++; } // Check if the second item is an item if (paramCount > ind && Item.class.isAssignableFrom(paramTypes[ind])) { objects[ind] = launcher; ind++; }// If there is stuff on the cursor use it for the rest of the params else if (launcher != null && launcher.isFloating()) { values = launcher.getText(); } String param = values; // convert the rest of the objects for (; ind < objects.length; ind++) { // check if its the last param and combine if (values.length() > 0 && ind == objects.length - 1) { param = values.trim(); // check if its a string if (param.length() > 0 && param.charAt(0) == '"') { int endOfString = param.indexOf('"', 1); if (endOfString > 0) { param = param.substring(1, endOfString); } } } else {// strip off the next value param = ParseValue(values); values = RemainingParams(values); } // convert the value to an object try { Object o = Conversion.Convert(paramTypes[ind], param); if (o == null) return null; objects[ind] = o; } catch (Exception e) { return null; } } return objects; } /** * Returns a string containing the remaining params after ignoring the first * one. * * @param params * a space sparated list of N parameters * @return the remaining N - 1 parameters */ public static String RemainingParams(String params) { if (params.length() == 0) return null; // remove leading and trailing spaces params = params.trim(); // if there are no more parameters, we are done if (params.indexOf(" ") < 0) { return ""; } // Check if we have a string parameter if (params.charAt(0) == '"') { int endOfString = params.indexOf('"', 1); if (endOfString > 0) { if (endOfString > params.length()) return ""; return params.substring(endOfString + 1).trim(); } } return params.substring(params.indexOf(" ")).trim(); } /** * Returns the first value in the space separated String of parameters * passed in. Strings are enclosed in double quotes. * * @param params * The String of space separated values * @return The first value in the String */ public static String ParseValue(String params) { if (params.length() == 0) return null; // remove leading and trailing spaces String param = params.trim(); // Check if we have a string parameter if (param.charAt(0) == '"') { int endOfString = param.indexOf('"', 1); if (endOfString > 0) return param.substring(1, endOfString); } // if there are no more parameters, we are done if (param.indexOf(" ") < 0) { return param; } return param.substring(0, param.indexOf(" ")); } /** * Separates the name of the given command from any parameters and returns * them Also deals with leading '@'s and leading and tailing whitespace. * * @param command * The String to separate out the Action or Agent name from * @return The name of the Action of Agent with parameters stripped off */ private static String getName(String command) { if (command.startsWith("@")) command = command.substring(1); command = command.trim(); if (command.indexOf(" ") < 0) return command; return command.substring(0, command.indexOf(" ")); } /** * Gets an uncapitalized font name and returns the capitalized font name. * The capitalized form can be used with the Font.decoded method to get a * corresponding Font object. * * @param fontName * a font name in mixed case * @return the correct capitalized form of the font name */ public static String getCapitalizedFontName(String fontName) { // Initialize the fonts if they have not already been loaded initFonts(); return _Fonts.get(fontName.toLowerCase()); } /** * Initialise the fontsList if it has not been done already */ private static void initFonts() { if (_Fonts.size() == 0) { String[] availableFonts = GraphicsEnvironment .getLocalGraphicsEnvironment() .getAvailableFontFamilyNames(); for (String s : availableFonts) { _Fonts.put(s.toLowerCase(), s); } } } public static HashMap getFonts() { initFonts(); return _Fonts; } public static Object PerformActionCatchErrors(Frame current, Item launcher, String command) { try { return PerformAction(current, launcher, command); } catch (RuntimeException e) { e.printStackTrace(); MessageBay.errorMessage("Action failed: " + e.getMessage()); } catch (Exception e) { e.printStackTrace(); MessageBay.errorMessage("Action failed: " + e.getClass().getSimpleName()); } return null; } /** * Gets the full class path for a widget with a given case insensitive name. * * @param widgetName * @return */ public static String getClassName(String widgetName) { return _IWs.get(widgetName.toLowerCase()); } static List getActions() { List actionNames = new LinkedList(); for (Method m : _Actions.values()) { StringBuilder sb = new StringBuilder(); sb.append(m.getName()); for (Class c : m.getParameterTypes()) { sb.append(" ").append(c.getSimpleName()); } actionNames.add(sb.toString()); } return actionNames; } static List getAgents() { List agentNames = new LinkedList(); for (String s : _JAGs.values()) { agentNames.add(s.substring(s.lastIndexOf('.') + 1)); } return agentNames; } }