Mega Code Archive

 
Categories / Java / Reflection
 

Invoke method through Java Reflection API

/*  * Copyright (c) 2000 David Flanagan.  All rights reserved.  * This code is from the book Java Examples in a Nutshell, 2nd Edition.  * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.  * You may study, use, and modify it for any non-commercial purpose.  * You may distribute it non-commercially as long as you retain this notice.  * For a commercial use license, or to purchase the book (recommended),  * visit http://www.davidflanagan.com/javaexamples2.  */ import java.awt.event.*; import java.beans.*; import java.lang.reflect.*; import java.io.*; import java.util.*; /**  * This class represents a Method, the list of arguments to be passed to that  * method, and the object on which the method is to be invoked. The invoke()  * method invokes the method. The actionPerformed() method does the same thing,  * allowing this class to implement ActionListener and be used to respond to  * ActionEvents generated in a GUI or elsewhere. The static parse() method  * parses a string representation of a method and its arguments.  */ public class Command implements ActionListener {   Method m; // The method to be invoked   Object target; // The object to invoke it on   Object[] args; // The arguments to pass to the method   // An empty array; used for methods with no arguments at all.   static final Object[] nullargs = new Object[] {};   /** This constructor creates a Command object for a no-arg method */   public Command(Object target, Method m) {     this(target, m, nullargs);   }   /**    * This constructor creates a Command object for a method that takes the    * specified array of arguments. Note that the parse() method provides    * another way to create a Command object    */   public Command(Object target, Method m, Object[] args) {     this.target = target;     this.m = m;     this.args = args;   }   /**    * Invoke the Command by calling the method on its target, and passing the    * arguments. See also actionPerformed() which does not throw the checked    * exceptions that this method does.    */   public void invoke() throws IllegalAccessException,       InvocationTargetException {     m.invoke(target, args); // Use reflection to invoke the method   }   /**    * This method implements the ActionListener interface. It is like invoke()    * except that it catches the exceptions thrown by that method and rethrows    * them as an unchecked RuntimeException    */   public void actionPerformed(ActionEvent e) {     try {       invoke(); // Call the invoke method     } catch (InvocationTargetException ex) { // but handle the exceptions       throw new RuntimeException("Command: "           + ex.getTargetException().toString());     } catch (IllegalAccessException ex) {       throw new RuntimeException("Command: " + ex.toString());     }   }   /**    * This static method creates a Command using the specified target object,    * and the specified string. The string should contain method name followed    * by an optional parenthesized comma-separated argument list and a    * semicolon. The arguments may be boolean, integer or double literals, or    * double-quoted strings. The parser is lenient about missing commas,    * semicolons and quotes, but throws an IOException if it cannot parse the    * string.    */   public static Command parse(Object target, String text) throws IOException {     String methodname; // The name of the method     ArrayList args = new ArrayList(); // Hold arguments as we parse them.     ArrayList types = new ArrayList(); // Hold argument types.     // Convert the string into a character stream, and use the     // StreamTokenizer class to convert it into a stream of tokens     StreamTokenizer t = new StreamTokenizer(new StringReader(text));     // The first token must be the method name     int c = t.nextToken(); // read a token     if (c != t.TT_WORD) // check the token type       throw new IOException("Missing method name for command");     methodname = t.sval; // Remember the method name     // Now we either need a semicolon or a open paren     c = t.nextToken();     if (c == '(') { // If we see an open paren, then parse an arg list       for (;;) { // Loop 'till end of arglist         c = t.nextToken(); // Read next token         if (c == ')') { // See if we're done parsing arguments.           c = t.nextToken(); // If so, parse an optional semicolon           if (c != ';')             t.pushBack();           break; // Now stop the loop.         }         // Otherwise, the token is an argument; figure out its type         if (c == t.TT_WORD) {           // If the token is an identifier, parse boolean literals,           // and treat any other tokens as unquoted string literals.           if (t.sval.equals("true")) { // Boolean literal             args.add(Boolean.TRUE);             types.add(boolean.class);           } else if (t.sval.equals("false")) { // Boolean literal             args.add(Boolean.FALSE);             types.add(boolean.class);           } else { // Assume its a string             args.add(t.sval);             types.add(String.class);           }         } else if (c == '"') { // If the token is a quoted string           args.add(t.sval);           types.add(String.class);         } else if (c == t.TT_NUMBER) { // If the token is a number           int i = (int) t.nval;           if (i == t.nval) { // Check if its an integer             // Note: this code treats a token like "2.0" as an int!             args.add(new Integer(i));             types.add(int.class);           } else { // Otherwise, its a double             args.add(new Double(t.nval));             types.add(double.class);           }         } else { // Any other token is an error           throw new IOException("Unexpected token " + t.sval               + " in argument list of " + methodname + "().");         }         // Next should be a comma, but we don't complain if its not         c = t.nextToken();         if (c != ',')           t.pushBack();       }     } else if (c != ';') { // if a method name is not followed by a paren       t.pushBack(); // then allow a semi-colon but don't require it.     }     // We've parsed the argument list.     // Next, convert the lists of argument values and types to arrays     Object[] argValues = args.toArray();     Class[] argtypes = (Class[]) types.toArray(new Class[argValues.length]);     // At this point, we've got a method name, and arrays of argument     // values and types. Use reflection on the class of the target object     // to find a method with the given name and argument types. Throw     // an exception if we can't find the named method.     Method method;     try {       method = target.getClass().getMethod(methodname, argtypes);     } catch (Exception e) {       throw new IOException("No such method found, or wrong argument "           + "types: " + methodname);     }     // Finally, create and return a Command object, using the target object     // passed to this method, the Method object we obtained above, and     // the array of argument values we parsed from the string.     return new Command(target, method, argValues);   }   public static void main(String[] args) throws IOException {     javax.swing.JFrame f = new javax.swing.JFrame("Command Test");     javax.swing.JButton b1 = new javax.swing.JButton("Tick");     javax.swing.JButton b2 = new javax.swing.JButton("Tock");     javax.swing.JLabel label = new javax.swing.JLabel("Hello world");     java.awt.Container pane = f.getContentPane();     pane.add(b1, java.awt.BorderLayout.WEST);     pane.add(b2, java.awt.BorderLayout.EAST);     pane.add(label, java.awt.BorderLayout.NORTH);     b1.addActionListener(Command.parse(label, "setText(\"tick\");"));     b2.addActionListener(Command.parse(label, "setText(\"tock\");"));     f.pack();     f.show();   } }