View Javadoc

1   /*
2    * Copyright 2006-2016 The JGUIraffe Team.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License")
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package net.sf.jguiraffe.gui.builder.action;
17  
18  import java.lang.reflect.InvocationHandler;
19  import java.lang.reflect.Method;
20  import java.lang.reflect.Proxy;
21  
22  import net.sf.jguiraffe.gui.builder.event.BuilderEvent;
23  import net.sf.jguiraffe.gui.builder.event.filter.AndEventFilter;
24  import net.sf.jguiraffe.gui.builder.event.filter.EventFilter;
25  
26  /**
27   * <p>
28   * A class that allows to combine actions with event listeners.
29   * </p>
30   * <p>
31   * This class can be used to create an event listener proxy for an arbitrary
32   * event listener interface that will delegate to a specified {@link FormAction}
33   * object. By defining an {@link EventFilter} it can be exactly specified, which
34   * event should cause the action to be invoked.
35   * </p>
36   * <p>
37   * The purpose of this class is to serve as a bridge between the event listener
38   * API and the action API. This is especially useful when event listeners are to
39   * be defined in builder scripts: Then it is easy to route to actions, which are
40   * defined by action tags anyway.
41   * </p>
42   * <p>
43   * Internally this class makes use of the Proxy mechanism supported by Java 1.3
44   * and higher. For the passed in event listener interface(s) a proxy object is
45   * created. Every invocation of one of the proxy's methods will cause the
46   * specified filter to be called to check whether the current event object
47   * matches the filter's criteria. If this is the case and if the action is
48   * enabled, its <code>execute()</code> method will be invoked.
49   * </p>
50   * <p>
51   * Instances of <code>ActionInvoker</code> should be created using the static
52   * factory methods. These methods return an object, which can be passed to one
53   * of the interfaces that was passed to the factory method. Then it can be
54   * registered as the corresponding event listener at the desired form component.
55   * </p>
56   *
57   * @author Oliver Heger
58   * @version $Id: ActionInvoker.java 205 2012-01-29 18:29:57Z oheger $
59   */
60  public class ActionInvoker implements InvocationHandler
61  {
62      /**
63       * Constant for a dummy filter that will be used when no filter is provided.
64       * This filter will accept all passed in objects.
65       */
66      private static final EventFilter DUMMY_FILTER = new AndEventFilter();
67  
68      /** Stores the action to be invoked. */
69      private final FormAction action;
70  
71      /** Stores the filter for events. */
72      private final EventFilter filter;
73  
74      /**
75       * Creates a new instance of <code>ActionInvoker</code> and initializes it
76       * with the action to be invoked. No event filter is used.
77       *
78       * @param action the action (must not be <b>null</b>)
79       */
80      ActionInvoker(FormAction action)
81      {
82          this(action, null);
83      }
84  
85      /**
86       * Creates a new instance of <code>ActionInvoker</code> and initializes it
87       * with the action to be invoked and the event filter.
88       *
89       * @param action the action (must not be <b>null</b>)
90       * @param filter the event filter; if <b>null</b>, all events will be
91       * accepted
92       */
93      ActionInvoker(FormAction action, EventFilter filter)
94      {
95          if (action == null)
96          {
97              throw new IllegalArgumentException("Action must not be null!");
98          }
99          this.action = action;
100         this.filter = (filter != null) ? filter : DUMMY_FILTER;
101     }
102 
103     /**
104      * Callback method that is invoked whenever a method on an associated event
105      * listener interface is called. This implementation will pass the first
106      * method argument (or <b>null</b> if there is none) to the associated
107      * event filter. If it is accepted by the filter, the associated action will
108      * be executed if it is enabled. If the first method argument is of type
109      * <code>{@link net.sf.jguiraffe.gui.builder.event.BuilderEvent}</code>,
110      * it will be passed to the action's <code>execute()</code> method;
111      * otherwise <b>null</b> will be passed.
112      *
113      * @param obj the current object instance
114      * @param meth the method to be invoked
115      * @param args the method arguments
116      * @return the method's return value (<b>null</b> in this case)
117      * @throws Throwable for all occurring exceptions
118      */
119     public Object invoke(Object obj, Method meth, Object[] args)
120             throws Throwable
121     {
122         Object testObj = (args == null || args.length < 1) ? null : args[0];
123         if (filter.accept(testObj))
124         {
125             if (action.isEnabled())
126             {
127                 BuilderEvent event = (testObj instanceof BuilderEvent) ? (BuilderEvent) testObj
128                         : null;
129                 action.execute(event);
130             }
131         }
132 
133         return null; // return value does not matter
134     }
135 
136     /**
137      * Creates an action invoker proxy for the specified listener interface that
138      * will invoke the given action whenever a method of the listener interface
139      * is called. The returned object can be casted to the specified listener
140      * class and then registered at a component.
141      *
142      * @param listenerClass the class of the listener interface
143      * @param action the action to be invoked (must not be <b>null</b>)
144      * @return the event listener proxy
145      */
146     public static Object create(Class<?> listenerClass, FormAction action)
147     {
148         return create(listenerClass, action, null);
149     }
150 
151     /**
152      * Creates an action invoker proxy for the specified listener interface that
153      * will invoke the given action when an event is triggered that is accepted
154      * by the passed in filter. The returned object can be casted to the
155      * specified listener class and then registered at a component.
156      *
157      * @param listenerClass the class of the listener interface
158      * @param action the action to be invoked (must not be <b>null</b>)
159      * @param filter the event filter (can be <b>null</b>, then all events will
160      * be accepted)
161      * @return the event listener proxy
162      */
163     public static Object create(Class<?> listenerClass, FormAction action,
164             EventFilter filter)
165     {
166         return create(new Class<?>[] {
167             listenerClass
168         }, action, filter);
169     }
170 
171     /**
172      * Creates an action invoker proxy that implements all the specified
173      * listener interfaces. It will invoke the given action when an event is
174      * triggered that is accepted by the passed in filter. The returned object
175      * can be casted to all the specified listener classes and then registered
176      * at a component.
177      *
178      * @param listenerClasses an array of the classes of the listener interfaces
179      * @param action the action to be invoked (must not be <b>null</b>)
180      * @param filter the event filter (can be <b>null</b>, then all events will
181      * be accepted)
182      * @return the event listener proxy
183      */
184     public static Object create(Class<?>[] listenerClasses, FormAction action,
185             EventFilter filter)
186     {
187         if (listenerClasses == null)
188         {
189             throw new IllegalArgumentException(
190                     "Listener classes must not be null!");
191         }
192         return Proxy.newProxyInstance(listenerClasses[0].getClassLoader(),
193                 listenerClasses, new ActionInvoker(action, filter));
194     }
195 }