]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/events/EventHandlerReflection.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.scenegraph / src / org / simantics / scenegraph / g2d / events / EventHandlerReflection.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 /*
13  *
14  * @author Toni Kalajainen
15  */
16 package org.simantics.scenegraph.g2d.events;
17
18 import java.lang.annotation.ElementType;
19 import java.lang.annotation.Retention;
20 import java.lang.annotation.RetentionPolicy;
21 import java.lang.annotation.Target;
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
24 import java.util.ArrayList;
25 import java.util.List;
26
27 /**
28  * @author Toni Kalajainen
29  */
30 public class EventHandlerReflection {
31
32     @Retention(RetentionPolicy.RUNTIME)
33     @Target(ElementType.METHOD)
34     public static @interface EventHandler {
35         int priority();
36     }
37
38     /**
39      * Scans an object with reflection for all event handler methods and returns
40      * an array of IEventHandler and priorities.
41      * <p>
42      * The corresponding class of obj must contain methods that meet the
43      * following criteria: 1) must return boolean 2) 1 argument, which is Event
44      * 3) has annotation EventHandler 4) may not throw any Exception 5) method
45      * must be accessible
46      * <p>
47      * Example:
48      * 
49      * @EventHandler(priority = Integer.MAX_VALUE) public boolean
50      *                        handleEvent(Event e) { return false; }
51      * 
52      * @param obj object to scan
53      * @return an array of painters and their priorities
54      */
55     @SuppressWarnings("unchecked")
56     public static EventHandlerDefinition[] getEventHandlers(final Object obj) {
57         List<EventHandlerDefinition> result = new ArrayList<EventHandlerDefinition>();
58         Class<?> clazz = obj.getClass();
59
60         for (final Method m : clazz.getMethods()) {
61             EventHandler anno = (EventHandler) m.getAnnotation(EventHandler.class);
62             if (anno == null)
63                 continue;
64
65             Class<?> returnType = m.getReturnType();
66             if (!returnType.equals(boolean.class))
67                 throw new RuntimeException(clazz.getName() + "." + m.getName() + " return type is invalid");
68
69             @SuppressWarnings("rawtypes")
70             Class[] argTypes = m.getParameterTypes();
71             if (argTypes.length != 1 || !Event.class.isAssignableFrom(argTypes[0]))
72                 throw new RuntimeException(clazz.getName() + "." + m.getName() + " invalid arguments");
73
74             @SuppressWarnings("rawtypes")
75             Class argClass = argTypes[0];
76
77             @SuppressWarnings("rawtypes")
78             Class[] exceptionTypes = m.getExceptionTypes();
79             if (exceptionTypes.length != 0)
80                 throw new RuntimeException(clazz.getName() + "." + m.getName() + " invalid exceptions");
81
82             int priority = anno.priority();
83
84             try {
85                 m.setAccessible(true);
86             } catch (Throwable t) {
87                 t.printStackTrace();
88                 continue;
89             }
90
91             final int eventMask = EventTypes.toTypeMask(argClass);
92
93             IEventHandler eventHandler = new IEventHandler() {
94                 @Override
95                 public int getEventMask() {
96                     return eventMask;
97                 }
98                 @Override
99                 public boolean handleEvent(Event e1) {
100                     try {
101                         return (Boolean) m.invoke(obj, e1);
102                     } catch (IllegalArgumentException e) {
103                         throw new Error(e);
104                     } catch (IllegalAccessException e) {
105                         throw new RuntimeException(e);
106                     } catch (InvocationTargetException e) {
107                         throw new RuntimeException(e.getCause());
108                     }
109                 }
110             };
111             EventHandlerDefinition handler = new EventHandlerDefinition(obj, eventHandler, priority, argClass);
112             result.add(handler);
113         }
114
115         return result.toArray(new EventHandlerDefinition[0]);
116     }
117
118     public final static class EventHandlerDefinition {
119         public final Object        obj;
120         public final IEventHandler origEventHandler;
121         public final IEventHandler eventHandler;
122         public final int           priority;
123         public final Class<Event>  eventSuperClass;
124
125         public EventHandlerDefinition(Object obj, IEventHandler eventHandler, int priority, Class<Event> eventSuperClass) {
126             this.obj = obj;
127             this.priority = priority;
128             this.eventSuperClass = eventSuperClass;
129             this.origEventHandler = eventHandler;
130             final int eventMask = EventTypes.toTypeMask(eventSuperClass);
131             if (eventSuperClass.equals(Event.class)) {
132                 this.eventHandler = eventHandler;
133             } else {
134                 this.eventHandler = new IEventHandler() {
135                     @Override
136                     public int getEventMask() {
137                         return eventMask;
138                     }
139
140                     @Override
141                     public boolean handleEvent(Event e) {
142                         // Event masking already taken care of, no need to check eventMask
143                         if (EventHandlerDefinition.this.eventSuperClass.isAssignableFrom(e.getClass()))
144                             return EventHandlerDefinition.this.origEventHandler.handleEvent(e);
145                         return false;
146                     }
147
148                     @Override
149                     public String toString() {
150                         return EventHandlerDefinition.this.toString();
151                     }
152                 };
153             }
154         }
155
156         @Override
157         public String toString() {
158             return String.format("[%11d] %s (%s)", priority, obj.getClass().getSimpleName(),
159                     eventSuperClass.getSimpleName());
160         }
161     }
162
163 }