View Javadoc

1   /*
2    * Copyright (c) 2004-2005, University Health Network.  All rights reserved. Distributed under the BSD 
3    * license (see http://opensource.org/licenses/bsd-license.php).
4    *  
5    * DoubleDispatch.java
6    *
7    * Created on 17-Dec-2004 at 8:58:46 PM
8    */
9   package ca.uhn.cache.util;
10  
11  import java.lang.reflect.InvocationTargetException;
12  import java.lang.reflect.Method;
13  import java.text.MessageFormat;
14  
15  
16  /***
17   * Implements double-dispatching using reflection. 
18   * 
19   * @author <a href="mailto:alexei.guevara@uhn.on.ca">Alexei Guevara</a>
20   * @version $Revision: 1.1 $ updated on $Date: 2005/01/24 22:51:49 $ by $Author: bryan_tripp $
21   */
22  public final class MultiDispatch {
23  
24      /***
25       */
26      private MultiDispatch() {
27      }
28      
29      /***
30       * Invoked a method in a callee using "double dispatche".
31       * 
32       * class A {}
33       * class A1 extends A {}
34       * class A2 extends A {}
35       * 
36       * class Callee {
37       *      Object methodA( A1 a1) {};
38       *      Object methodA( A2 a2) {};
39       * }
40       * 
41       * A a = new A1();
42       * 
43       * Callee callee = new Callee();
44       * 
45       * //the next statement will invoke Callee.methodA( A1 a1 ) 
46       * Object retVal = DoubleDispatch.dispatch( callee, "methodA", a );
47       * 
48       * @param theCallee The callee instance.
49       * @param theCalleeMethodName The callee method name.
50       * @param theArg1 The caller.
51       * @return The return value of the called method.
52       * 
53       * @throws Throwable ...
54       * 
55       * @precondition theCallee has method with signature $theCalleeMethodName( theArg1.class )
56       */
57      public static final Object dispatch( Object theCallee, String theCalleeMethodName, Object theArg1 ) 
58          throws Throwable {
59          
60          //TODO make is throw the checked exception DoubleDispatchException
61          Object retVal = null;
62          
63          Class arg1Clazz = theArg1.getClass();
64          Class calleeClazz = theCallee.getClass();
65  
66          try {
67              Method calleeMethod = findMethod( calleeClazz, theCalleeMethodName, new Class[] { arg1Clazz } );
68              if ( calleeMethod == null ) {
69                  throw new NoSuchMethodException( 
70                          MessageFormat.format( 
71                                  "* {0}.{1}( {2} )", 
72                                  new Object[] { calleeClazz.getName(), theCalleeMethodName, arg1Clazz.getName() } ) );
73              }
74              try {
75                  retVal = calleeMethod.invoke( theCallee, new Object[] { theArg1 } );
76              }
77              catch (IllegalArgumentException e) {
78                  throw e;
79              }
80              catch (IllegalAccessException e) {
81                  throw e;
82              }
83              catch (InvocationTargetException e) {
84                  throw e.getCause();
85              }
86          }
87          catch (SecurityException e) {
88              throw e;
89          }
90          catch (NoSuchMethodException e) {
91              throw e;
92          }
93          
94          return retVal;
95      }
96  
97      /***
98       * @param theCallee The callee instance.
99       * @param theCalleeMethodName The callee method name.
100      * @param theArg1 The arg 1.
101      * @param theArg2 The arg 2. 
102      * @return The return value of the called method.
103      * 
104      * @throws Throwable ...
105      * 
106      * @precondition theCallee has method with signature $theCalleeMethodName( theArg1.class )
107      */
108     public static final Object dispatch( Object theCallee, String theCalleeMethodName, Object theArg1, Object theArg2 ) 
109         throws Throwable {
110         
111         //TODO make is throw the checked exception DoubleDispatchException
112         Object retVal = null;
113         
114         Class arg1Clazz = (theArg1==null) ? (null) : (theArg1.getClass());
115         Class arg2Clazz = (theArg2==null) ? (null) : (theArg2.getClass());
116         Class calleeClazz = theCallee.getClass();
117 
118         try {
119             Method calleeMethod = findMethod( calleeClazz, theCalleeMethodName, new Class[] { arg1Clazz, arg2Clazz } );
120             if ( calleeMethod == null ) {
121                 throw new NoSuchMethodException( 
122                         MessageFormat.format( 
123                                 "* {0}.{1}( {2}, {3} )", 
124                                 new Object[] { 
125                                         calleeClazz.getName(), 
126                                         theCalleeMethodName, 
127                                         (arg1Clazz==null) ? ("*") : (arg1Clazz.getName()),
128                                         (arg2Clazz==null) ? ("*") : (arg2Clazz.getName())} ) );
129             }
130             try {
131                 retVal = calleeMethod.invoke( theCallee, new Object[] { theArg1, theArg2 } );
132             }
133             catch (IllegalArgumentException e) {
134                 throw e;
135             }
136             catch (IllegalAccessException e) {
137                 throw e;
138             }
139             catch (InvocationTargetException e) {
140                 throw e.getCause();
141             }
142         }
143         catch (SecurityException e) {
144             throw e;
145         }
146         catch (NoSuchMethodException e) {
147             throw e;
148         }
149         
150         return retVal;
151     }
152     
153     private static Method findMethod( Class theClazz, String theMethodName, Class[] theParameterTypes ) {
154         Method retVal = null;
155         
156         Method[] methods = theClazz.getMethods();
157         methods:for (int i = 0; i < methods.length; i++) {
158             Method method = methods[i];
159             
160             Class[] methodParamTypes = method.getParameterTypes();
161             
162             if ( !method.getName().equals( theMethodName ) ) {
163                 continue methods;
164             }
165 
166             if ( method.getParameterTypes().length != theParameterTypes.length ) {
167                 continue methods;
168             }
169             
170             parameters:for (int j = 0; j < methodParamTypes.length; j++) {
171                 if ( theParameterTypes[j] == null ) {
172                     continue;
173                 }
174                 
175                 Class methodParamType = methodParamTypes[j];
176                 if ( !theParameterTypes[j].equals( methodParamType ) ) {
177                     continue methods;
178                 }
179             }
180             retVal = method;
181             break;
182         }
183         
184         return retVal;
185     }
186 
187 }