001    /* JAPI - (Yet anothr (hopefully) useful) Java API
002     *
003     * Copyright (C) 2004-2006 Christian Hujer
004     *
005     * This program is free software; you can redistribute it and/or
006     * modify it under the terms of the GNU General Public License as
007     * published by the Free Software Foundation; either version 2 of the
008     * License, or (at your option) any later version.
009     *
010     * This program is distributed in the hope that it will be useful, but
011     * WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013     * General Public License for more details.
014     *
015     * You should have received a copy of the GNU General Public License
016     * along with this program; if not, write to the Free Software
017     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
018     * 02111-1307, USA.
019     */
020    
021    package net.sf.japi.swing;
022    
023    import java.awt.event.ActionEvent;
024    import java.lang.reflect.InvocationTargetException;
025    import java.lang.reflect.Method;
026    import javax.swing.AbstractAction;
027    import javax.swing.Icon;
028    
029    /** Action implementation which invokes the desired method using Reflection.
030     * Usage example:
031     * <pre>
032     *  class SomeClass {
033     *      SomeClass() {
034     *          ReflectionAction action = new ReflectionAction();
035     *          action.putValue(REFLECTION_TARGET, this);
036     *          action.putValue(REFLECTION_METHOD_NAME, "myAction");
037     *          new JMenuItem(action);
038     *      }
039     *      void myAction() {
040     *          // do something
041     *      }
042     *  }
043     * </pre>
044     * Note that because of Reflection this Action is slightly slower than implementing your own Action instance, but in most cases this really does not
045     * matter at all.
046     * @author <a href="mailto:Christian.Hujer@itcqis.com">Christian Hujer</a>
047     */
048    public final class ReflectionAction extends AbstractAction {
049    
050        /** The key used for storing the target object to invoke the method on.
051         * Value Type: {@link Object}.
052         */
053        public static final String REFLECTION_TARGET = "ReflectionTarget";
054    
055        /** The key used for storing the method name to use when searching for a method using reflection.
056         * Value Type: {@link String} (checked).
057         */
058        public static final String REFLECTION_METHOD_NAME = "ReflectionMethodName";
059    
060        /** The key used for storing the method object to use when invoking the method.
061         * Value Type: {@link Method} (checked).
062         */
063        public static final String REFLECTION_METHOD = "ReflectionMethod";
064    
065        /** Serial Version. */
066        @SuppressWarnings({"AnalyzingVariableNaming"})
067        private static final long serialVersionUID = 1L;
068    
069        /** Create an uninitialized ReflectionAction. */
070        public ReflectionAction() {
071        }
072    
073        /** Create a ReflectionAction with method and target.
074         * @param methodName Name of method to invoke
075         * @param target Target object to invoke method at
076         */
077        public ReflectionAction(final String methodName, final Object target) {
078            putValue(REFLECTION_METHOD_NAME, methodName);
079            putValue(REFLECTION_TARGET, target);
080        }
081    
082        /** {@inheritDoc}
083         * This implementation checks the type of <var>newValue</var> if the <var>key</var> is {@link #REFLECTION_METHOD_NAME} or {@link
084         * #REFLECTION_METHOD}, so you'll know of errors quite soon.
085         * @throws IllegalArgumentException if <var>newValue</var> is of the wrong type
086         */
087        @Override public void putValue(final String key, final Object newValue) throws IllegalArgumentException {
088            if (REFLECTION_METHOD_NAME.equals(key)) {
089                if (!(newValue == null || newValue instanceof String)) {
090                    throw new IllegalArgumentException("Value for key REFLECTION_METHOD_NAME must be of type " + String.class.getName() + " but was " + newValue.getClass().getName());
091                }
092                putValue(REFLECTION_METHOD, null);
093            }
094            if (REFLECTION_METHOD.equals(key)) {
095                if (!(newValue == null || newValue instanceof Method)) {
096                    if (newValue instanceof String) {
097                        throw new IllegalArgumentException("Value for key REFLECTION_METHOD must be of type " + Method.class.getName() + " but was " + String.class.getName() + " so you might want to use the key REFLECTION_METHOD_NAME instead.");
098                    } else {
099                        throw new IllegalArgumentException("Value for key REFLECTION_METHOD must be of type " + Method.class.getName() + " but was " + newValue.getClass().getName());
100                    }
101                }
102            }
103            if (REFLECTION_TARGET.equals(key)) {
104                if (newValue == null) {
105                    putValue(REFLECTION_METHOD, null);
106                }
107            }
108            super.putValue(key, newValue);
109        }
110    
111        /** Defines an <code>Action</code> object with the specified description string and a the specified icon.
112         * @param name description string
113         * @param icon icon
114         * @param methodName Name of method to invoke
115         * @param target Target object to invoke method at
116         */
117        public ReflectionAction(final String name, final Icon icon, final String methodName, final Object target) {
118            super(name, icon);
119            putValue(REFLECTION_METHOD_NAME, methodName);
120            putValue(REFLECTION_TARGET, target);
121        }
122    
123        /** {@inheritDoc}
124         * The implementation of this method first looks whether the Action is enabled.
125         * If it isn't, the method simply returns.
126         * Otherwise, instance and method are looked up.
127         * If both are null, the method again returns.
128         * If the method is null, it is reflected upon the instance usign the method name. If the method name is null, the method returns.
129         * Finally the method is invoked upon the instance, which may be null for static methods.
130         * @throws RuntimeException with cause in case the invocation of the method threw an exception
131         */
132        public void actionPerformed(final ActionEvent e) {
133            if (!isEnabled()) { return; }
134            final Object instance = getValue(REFLECTION_TARGET);
135            Method method = (Method) getValue(REFLECTION_METHOD);
136            if (instance == null && method == null) {
137                return;
138            } // it's okay if method is not null but instance is null because it might be a static method?
139            if (method == null) {
140                final String methodName = (String) getValue(REFLECTION_METHOD_NAME);
141                if (methodName == null) {
142                    return;
143                }
144                try {
145                    method = instance.getClass().getMethod(methodName);
146                    putValue(REFLECTION_METHOD, method);
147                } catch (final NoSuchMethodException ex) {
148                    assert false : ex;
149                    return;
150                }
151            }
152            try {
153                method.invoke(instance);
154            } catch (final IllegalAccessException ex) {
155                assert false : ex;
156            } catch (final InvocationTargetException ex) {
157                throw new RuntimeException(ex.getCause());
158            }
159        }
160    
161        /** {@inheritDoc} */
162        @Override protected Object clone() throws CloneNotSupportedException {
163            return super.clone();
164        }
165    
166    } // class ReflectionAction