View Javadoc

1   /* This file is part of COPAL (COntext Provisioning for All).
2    *
3    * COPAL is a part of SM4All (Smart hoMes for All) project.
4    *
5    * COPAL is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU Lesser General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * COPAL is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General Public License
16   * along with COPAL. If not, see <http://www.gnu.org/licenses/>.
17   */
18  package at.ac.tuwien.infosys.sm4all.copal.api.listener;
19  
20  import java.lang.reflect.InvocationTargetException;
21  import java.lang.reflect.Method;
22  import java.lang.reflect.Modifier;
23  import java.text.MessageFormat;
24  import java.util.Collections;
25  import java.util.HashSet;
26  import java.util.Set;
27  import at.ac.tuwien.infosys.sm4all.copal.api.event.ContextEvent;
28  
29  /**
30   * Invoker of an {@link Events} {@link Method}. For method to be considered an
31   * {@link Events} {@link Method} all these conditions must be met:
32   * <ol>
33   * <li>{@link Method} must be annotated with {@link Event} or {@link Events}.</li>
34   * <li>{@link Method} must be public.</li>
35   * <li>{@link Method} must have exactly one parameter.</li>
36   * <li>The parameter of {@link Method} must be of type {@link ContextEvent} or a
37   * subclass of it.</li>
38   * </ol>
39   * 
40   * @author sanjin
41   */
42  public class EventsMethod {
43  
44      private final Object listener;
45      private final Method method;
46      private final Set<String> types;
47      private final Class<?> parameter;
48  
49      /**
50       * Creates instance of invoker of specified {@link Events} {@link Method} on
51       * specified listener.
52       * 
53       * @param listener the wrapped annotated listener.
54       * @param method the {@link Events} {@link Method}.
55       * @throws NullPointerException if specified wrapped annotated listener or
56       *         {@link Method} is <code>null</code>.
57       * @throws IllegalArgumentException if specified {@link Method} has no
58       *         {@link Event} or {@link Events} annotation, is not public, has
59       *         zero or more than one parameter, or parameter is not
60       *         {@link ContextEvent} or a subclass of it.
61       */
62      public EventsMethod(final Object listener, final Method method) {
63          super();
64  
65          if (null == listener) {
66              throw new NullPointerException("Listener cannot be null.");
67          }
68          if (null == method) {
69              throw new NullPointerException("Method cannot be null.");
70          }
71  
72          final String methodName = method.getName();
73          final Event event = method.getAnnotation(Event.class);
74          final Events events = method.getAnnotation(Events.class);
75          if ((null == event) && (null == events)) {
76              throw new IllegalArgumentException(MessageFormat.format(
77                      "Method ''{0}'' has no Event nor Events annotation.",
78                      methodName));
79          }
80          if ((null != event) && (null != events)) {
81              throw new IllegalArgumentException(MessageFormat.format(
82                      "Method ''{0}'' has both Event and Events annotations.",
83                      methodName));
84          }
85          if (!Modifier.isPublic(method.getModifiers())) {
86              throw new IllegalArgumentException(MessageFormat.format(
87                      "Events method ''{0}'' is not public.", methodName));
88          }
89  
90          final Class<?>[] parameters = method.getParameterTypes();
91          if (parameters.length > 1) {
92              throw new IllegalArgumentException(MessageFormat.format(
93                      "Events method ''{0}'' has more than one parameter.",
94                      methodName));
95          }
96          if (0 == parameters.length) {
97              throw new IllegalArgumentException(MessageFormat.format(
98                      "Events method ''{0}'' has no parameters.", methodName));
99          }
100         if (!ContextEvent.class.isAssignableFrom(parameters[0])) {
101             throw new IllegalArgumentException(
102                     MessageFormat.format(
103                             "Parameter of Events method ''{0}'' is not ContextEvent or its subclass.",
104                             methodName));
105         }
106 
107         this.listener = listener;
108         this.method = method;
109         if (null == event) {
110             this.types = toTypes(events);
111         } else {
112             this.types = toTypes(event);
113         }
114         this.parameter = parameters[0];
115     }
116 
117     /**
118      * Returns the name of this {@link Events} {@link Method}.
119      * 
120      * @return the name of this {@link Events} {@link Method}.
121      * @see Method#getName()
122      */
123     public String getName() {
124         return this.method.getName();
125     }
126 
127     /**
128      * Returns types of {@link ContextEvent}s caught by this {@link Events}
129      * {@link Method}.
130      * 
131      * @return types of {@link ContextEvent}s.
132      * @see Events#value()
133      * @see Event#type()
134      */
135     public String[] getTypes() {
136         return this.types.toArray(new String[this.types.size()]);
137     }
138 
139     /**
140      * Returns type of parameter of this {@link Events} {@link Method}.
141      * 
142      * @return type of parameter.
143      * @see Method#getParameterTypes()
144      */
145     public Class<?> getParameter() {
146         return this.parameter;
147     }
148 
149     /**
150      * Returns if specified {@link ContextEvent} can be passed as parameter to
151      * this {@link Events} {@link Method}.
152      * 
153      * @param event the {@link ContextEvent}.
154      * @return if specified {@link ContextEvent} can be passed as parameter.
155      * @throws NullPointerException if specified {@link ContextEvent} is
156      *         <code>null</code>.
157      */
158     public boolean canBeInvokedWith(final ContextEvent event) {
159         if (null == event) {
160             throw new NullPointerException("Event cannot be null.");
161         }
162 
163         return (this.types.contains(Event.ANY_TYPE) || this.types.contains(event.getType().getName()))
164                 && this.parameter.isAssignableFrom(event.getClass());
165     }
166 
167     /**
168      * Invokes the underlying {@link Events} {@link Method} with specified
169      * {@link ContextEvent}.
170      * 
171      * @param event the {@link ContextEvent}.
172      * @throws IllegalAccessException if this {@link Events} {@link Method}
173      *         enforces Java language access control and the underlying method
174      *         is inaccessible.
175      * @throws InvocationTargetException if the underlying {@link Events}
176      *         {@link Method} throws an {@link Exception}.
177      * @throws NullPointerException if specified {@link ContextEvent} is
178      *         <code>null</code>.
179      * @throws IllegalArgumentException if specified {@link ContextEvent} can
180      *         not be passed as parameter to this {@link Events} {@link Method}
181      *         i.e. the {@link #canBeInvokedWith(ContextEvent)} method returns
182      *         <code>false</code>.
183      */
184     public void invoke(final ContextEvent event) throws IllegalAccessException,
185             InvocationTargetException {
186         if (null == event) {
187             throw new NullPointerException("Event cannot be null.");
188         }
189 
190         if (canBeInvokedWith(event)) {
191             this.method.invoke(this.listener, event);
192         } else {
193             throw new IllegalArgumentException("Wrong event.");
194         }
195     }
196 
197     private static Set<String> toTypes(final Events events) {
198         final Set<String> result = new HashSet<String>();
199 
200         for (final Event event : events.value()) {
201             result.addAll(toTypes(event));
202         }
203 
204         return Collections.unmodifiableSet(result);
205     }
206 
207     private static Set<String> toTypes(final Event event) {
208         final Set<String> result = new HashSet<String>();
209 
210         result.add(event.type());
211 
212         return Collections.unmodifiableSet(result);
213     }
214 }