KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > beehive > controls > api > events > EventRef


1 package org.apache.beehive.controls.api.events;
2 /*
3  * Copyright 2004 The Apache Software Foundation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * $Header:$
18  */

19
20 import java.lang.reflect.Method JavaDoc;
21 import java.util.HashMap JavaDoc;
22 import java.util.WeakHashMap JavaDoc;
23
24 /**
25  * The EventRef class represents a reference to a specific Control event. EventRefs can
26  * be used to fire external events into a Control, in contexts where the event source may
27  * not share the associated EventSet class instance with the event target, or even have
28  * access to the EventSet class itself.
29  * <p>
30  * It is roughly equivalent to the java.lang.reflect.Method object that refers to a method
31  * on an EventSet interface, but has several additional properties:
32  * <ul>
33  * <li>It is serializable, so can be persisted/restored or passed across the wire</li>
34  * <li>It supports materializing the EventRef back to a Method reference in a way that allows
35  * EventRefs to be passed across class loaders</li>
36  * <li>It can be constructed in contexts where a reference to the actual EventSet class might
37  * not be available (using a String event descriptor format to describe events)</li>
38  * </ul>
39  */

40 public class EventRef implements java.io.Serializable JavaDoc
41 {
42     //
43
// Static helper map to go from a primitive type to the type descriptor string
44
//
45
static private HashMap JavaDoc<Class JavaDoc,String JavaDoc> _primToType = new HashMap JavaDoc<Class JavaDoc,String JavaDoc>();
46     static
47     {
48         _primToType.put(Integer.TYPE, "I");
49         _primToType.put(Boolean.TYPE, "Z");
50         _primToType.put(Byte.TYPE, "B");
51         _primToType.put(Character.TYPE, "C");
52         _primToType.put(Short.TYPE, "S");
53         _primToType.put(Long.TYPE, "J");
54         _primToType.put(Float.TYPE, "F");
55         _primToType.put(Double.TYPE, "D");
56         _primToType.put(Void.TYPE, "V");
57     }
58
59     /**
60      * Constructs a new EventRef based upon a Method reference. The input method must be one
61      * that is declared within a Control EventSet interface.
62      * @param eventMethod the Method associated with the event
63      */

64     public EventRef(Method JavaDoc eventMethod)
65     {
66         _method = eventMethod;
67         _descriptor = computeEventDescriptor(eventMethod);
68     }
69
70     /**
71      * Constructs a new EventRef using an event descriptor string. The format of this string
72      * is:
73      * <pre>
74      * <eventSet>.<eventName><eventDescriptor>
75      * </pre>
76      * where <i>eventSet</i> refers to the fully qualified name of the EventSet class,
77      * <i>eventName</i> refers to the name of the event Method, and <i>eventDescriptor</i>
78      * describes the event argument and return types using the method descriptor format
79      * defined in the Java Language Specification.
80      * <p>
81      * For example, given the following EventSet interface:
82      * <pre>
83      * <sp>@ControlInterface
84      * public interface MyControl
85      * {
86      * <sp>@EventSet
87      * public interface MyEvents
88      * {
89      * public String myEvent(int arg0, Object arg2);
90      * }
91      * }
92      * </pre>
93      * the eventDescriptor for myEvent would be:
94      * <pre>
95      * MyControl.MyEvents.myEvent(ILjava/lang/Object;)Ljava/lang/String;
96      * </pre>
97      * @param eventDescriptor the event descriptor string associated with the event
98      */

99     public EventRef(String JavaDoc eventDescriptor)
100     {
101         _descriptor = eventDescriptor;
102     }
103
104     /**
105      * Returns the event descriptor string associated with the EventRef.
106      * @param controlInterface the ControlInterface
107      */

108     public String JavaDoc getEventDescriptor(Class JavaDoc controlInterface)
109     {
110         //
111
// NOTE: The input controlInterface is currently unused, but included to
112
// enable downstream optimization of serialization representation. See the
113
// OPTIMIZE comment below for more details. If implemented, the interface
114
// is needed to reverse the transformation from a hash back to a method or
115
// descriptor.
116
//
117
if (_descriptor == null)
118             _descriptor = computeEventDescriptor(_method);
119
120         return _descriptor;
121     }
122
123     /**
124      * Helper method that computes the event descriptor sting for a method
125      */

126     private String JavaDoc computeEventDescriptor(Method JavaDoc method)
127     {
128         StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
129
130         // Add event class and method name
131
sb.append(method.getDeclaringClass().getName());
132         sb.append(".");
133         sb.append(method.getName());
134
135         // Add event arguments
136
Class JavaDoc [] parms = method.getParameterTypes();
137         sb.append("(");
138         for (int i = 0; i < parms.length; i++)
139             appendTypeDescriptor(sb, parms[i]);
140         sb.append(")");
141
142         // Add event return type
143
appendTypeDescriptor(sb, method.getReturnType());
144
145         return sb.toString();
146     }
147
148     /**
149      * Helper method that appends a type descriptor to a StringBuilder. Used
150      * while accumulating an event descriptor string.
151      */

152     private void appendTypeDescriptor(StringBuilder JavaDoc sb, Class JavaDoc clazz)
153     {
154         if (clazz.isPrimitive())
155             sb.append(_primToType.get(clazz));
156         else if (clazz.isArray())
157             sb.append(clazz.getName().replace('.','/'));
158         else
159         {
160             sb.append("L");
161             sb.append(clazz.getName().replace('.','/'));
162             sb.append(";");
163         }
164     }
165     
166     /**
167      * Returns the event Method associated with this EventRef.
168      */

169     public Method JavaDoc getEventMethod(Class JavaDoc controlInterface)
170     {
171         //
172
// If we already hold a method reference and its loader matches up with the input
173
// interface, then just return it.
174
//
175
if (_method != null &&
176             _method.getDeclaringClass().getClassLoader().equals(controlInterface.getClassLoader()))
177             return _method;
178
179         //
180
// Otherwise, obtain the mapping from descriptors to methods, and use it to
181
// convert back to a method.
182
//
183
String JavaDoc eventDescriptor = getEventDescriptor(controlInterface);
184         HashMap JavaDoc<String JavaDoc,Method JavaDoc> descriptorMap = getDescriptorMap(controlInterface);
185         if (!descriptorMap.containsKey(eventDescriptor))
186         {
187             throw new IllegalArgumentException JavaDoc("Control interface " + controlInterface +
188                                                " does not contain an event method that " +
189                                                " corresponds to " + eventDescriptor);
190         }
191         return descriptorMap.get(eventDescriptor);
192     }
193
194     /**
195      * A WeakHashMap used to cache the event descriptor-to-Method mapping for control
196      * interfaces.
197      */

198     static private WeakHashMap JavaDoc<Class JavaDoc, HashMap JavaDoc<String JavaDoc,Method JavaDoc>> _descriptorMaps =
199                                     new WeakHashMap JavaDoc<Class JavaDoc, HashMap JavaDoc<String JavaDoc,Method JavaDoc>>();
200
201     private HashMap JavaDoc<String JavaDoc,Method JavaDoc> getDescriptorMap(Class JavaDoc controlInterface)
202     {
203         //
204
// If the local cache has the mapping, then return it.
205
//
206
HashMap JavaDoc<String JavaDoc,Method JavaDoc> descMap = _descriptorMaps.get(controlInterface);
207         if (descMap == null)
208         {
209             //
210
// Compute the mapping from event descriptors to event methods, using reflection
211
//
212
descMap = new HashMap JavaDoc<String JavaDoc, Method JavaDoc>();
213             Class JavaDoc [] innerClasses = controlInterface.getClasses();
214             for (int i = 0; i < innerClasses.length; i++)
215             {
216                 if (!innerClasses[i].isInterface() ||
217                     !innerClasses[i].isAnnotationPresent(EventSet.class))
218                     continue;
219
220                 Method JavaDoc [] eventMethods = innerClasses[i].getMethods();
221                 for (int j = 0; j < eventMethods.length; j++)
222                     descMap.put(computeEventDescriptor(eventMethods[j]), eventMethods[j]);
223             }
224             _descriptorMaps.put(controlInterface, descMap);
225         }
226         return descMap;
227     }
228
229
230     /**
231      * Two EventRefs are equal if the method descriptor string associated with them is equal
232      */

233     public boolean equals(Object JavaDoc obj)
234     {
235         if (obj == null || !(obj instanceof EventRef))
236             return false;
237
238         return _descriptor.equals(((EventRef)obj)._descriptor);
239     }
240
241     public String JavaDoc toString()
242     {
243         return "EventRef: " + _descriptor;
244     }
245
246     //
247
// OPTIMIZE: A more efficient internal representation for serialization/wire purposes
248
// would be to compute a hash of the descriptor string (ala RMI opnums), that could be
249
// reconstituted on the other end, given a candidate ControlInterface. The public APIs
250
// are structured to support this downstream optimization.
251
//
252
private String JavaDoc _descriptor;
253     transient private Method JavaDoc _method;
254 }
255
Popular Tags