View Javadoc

1   /*
2    * Copyright 2010-2013 the original author or authors.
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  
17  package org.callbackparams.internal.template;
18  
19  import java.lang.reflect.Constructor;
20  import java.lang.reflect.Field;
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Method;
23  import java.lang.reflect.Modifier;
24  import java.util.ArrayList;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.ListIterator;
28  import org.callbackparams.factory.CallbackFactory;
29  
30  /**
31   * @author Henrik Kaipe
32   */
33  public abstract class CallbackRef {
34  
35      private final Class paramClass;
36      private final AnnotationSpecifiedCallbacks annotationSpecified;
37      private final CallbackFactory secondaryFactory;
38  
39      public static CallbackRef newInstance(Method m, int paramIndex) {
40          return new CallbackRef(m, paramIndex) {};
41      }
42  
43      static CallbackRef newInstance(Field f) {
44          return new CallbackRef(f) {};
45      }
46  
47      interface CallbackFactoryFinder {
48          CallbackFactory find(Class interf);
49      }
50  
51      /**
52       * Search for a field that is declared as CallbackFactory ...
53       */
54      private static final CallbackFactoryFinder FACTORY_FIELD_FINDER =
55              new CallbackFactoryFinder() {
56          public CallbackFactory find(Class interf) {
57  
58              final Field[] fields = interf.getDeclaredFields();
59              for (int i = 0 ; i < fields.length ; ++i) {
60                  final Field f = fields[i];
61  
62                  try {
63                      f.setAccessible(true);
64                  } catch (SecurityException ignore) {
65                      /* Let's try to continue anyway ... */
66                  }
67  
68                  if (CallbackFactory.class.isAssignableFrom(f.getType())) {
69                      try {
70                          return (CallbackFactory) f.get(null);
71                      } catch (Exception x) {
72                          throw new Error(x);
73                      }
74                  }
75              }
76              return null;
77          }
78      };
79  
80      private static CallbackFactoryFinder findByInnerClassConstructor(
81              final Class[] constructorParamTypes,
82              final Object[] constructorArgs) {
83  
84          return new CallbackFactoryFinder() {
85              public CallbackFactory find(Class interf) {
86  
87                  final Class[] nestedClasses = interf.getDeclaredClasses();
88                  for (int i = 0 ; i < nestedClasses.length ; ++i) {
89                      final Class nested = nestedClasses[i];
90                      if (false == CallbackFactory.class.isAssignableFrom(nested)
91                              || Modifier.isAbstract(nested.getModifiers())) {
92                          continue;
93                      }
94  
95                      Constructor constructor = null;
96                      try {
97                          constructor = nested
98                              .getDeclaredConstructor(constructorParamTypes);
99                          constructor.setAccessible(true);
100 
101                     } catch (NoSuchMethodException x) {
102                         continue;
103                     } catch (SecurityException ignore) {
104                         /* Let's hope it will work anyway ... */
105                     }
106 
107                     try {
108                         return (CallbackFactory) constructor
109                                 .newInstance(constructorArgs);
110 
111                     } catch (Exception noGood) {
112                         Throwable toThrow;
113                         if (noGood instanceof InvocationTargetException) {
114                             toThrow = ((InvocationTargetException)noGood)
115                                     .getTargetException();
116                         } else {
117                             toThrow = noGood;
118                         }
119                         if (toThrow instanceof RuntimeException) {
120                             throw (RuntimeException)toThrow;
121                         } else {
122                             throw new Error(toThrow);
123                         }
124                     }
125                 }
126                 return null;
127             }
128         };
129     }
130 
131     private static final CallbackFactory NULL_FACTORY = new CallbackFactory() {
132         public Object asCallback(Object callbackRecordElement) {
133             return null;
134         }
135     };
136 
137     private static Iterator interfaces(Class interf) {
138         final List interfaces = new ArrayList();
139         interfaces.add(interf);
140 
141         return new Iterator() {
142             Iterator iter = interfaces.iterator();
143 
144             public boolean hasNext() {
145                 return false == interfaces.isEmpty();
146             }
147             public Object next() {
148                 try {
149                     return iter.next();
150                 } finally {
151                     if (false == iter.hasNext()) {
152                         refreshInterfacesList();
153                         iter = interfaces.iterator();
154                     }
155                 }
156             }
157             public void remove() {
158                 throw new UnsupportedOperationException("Not supported yet.");
159             }
160 
161             void refreshInterfacesList() {
162                 ListIterator li = interfaces.listIterator();
163                 while (li.hasNext()) {
164                     final Class old = (Class) li.next();
165                     li.remove();
166                     Class[] superInterfaces = old.getInterfaces();
167                     for (int i = 0; i < superInterfaces.length; ++i) {
168                         li.add(superInterfaces[i]);
169                     }
170                 }
171             }
172         };
173     }
174 
175     private CallbackFactory lookupDeclaredCallbackFactory(Field f) {
176         return lookupDeclaredCallbackFactory(findByInnerClassConstructor(
177                 new Class[] {Field.class}, new Object[] {f}));
178     }
179 
180     private CallbackFactory lookupDeclaredCallbackFactory(
181             Method m, int paramIndex) {
182         return lookupDeclaredCallbackFactory(findByInnerClassConstructor(
183                 new Class[] {Method.class, int.class},
184                 new Object[] {m, new Integer(paramIndex)}));
185     }
186 
187     private CallbackFactory lookupDeclaredCallbackFactory(
188             CallbackFactoryFinder finderBackup) {
189         CallbackFactoryFinder[] finders = {
190             FACTORY_FIELD_FINDER, finderBackup
191         };
192 
193         for (Iterator iter = interfaces(paramClass) ; iter.hasNext() ;) {
194             final Class interf = (Class) iter.next();
195 
196             for (int i = 0 ; i < finders.length ; ++i) {
197                 final CallbackFactoryFinder factoryFinder = finders[i];
198 
199                 CallbackFactory factory = factoryFinder.find(interf);
200                 if (null != factory) {
201                     return factory;
202                 }
203             }
204         }
205 
206         return NULL_FACTORY;
207     }
208 
209     CallbackRef(Method m, int paramIndex) {
210         this.paramClass = m.getParameterTypes()[paramIndex];
211         possibleDeprecationWarning(m);
212         this.annotationSpecified =
213                 AnnotationSpecifiedCallbacks.forCallbackRef(m, paramIndex);
214         this.secondaryFactory = lookupDeclaredCallbackFactory(m, paramIndex);
215     }
216 
217     CallbackRef(Field f) {
218         this.paramClass = f.getType();
219         possibleDeprecationWarning(f);
220         this.annotationSpecified =
221                 AnnotationSpecifiedCallbacks.forCallbackRef(f);
222         this.secondaryFactory = lookupDeclaredCallbackFactory(f);
223     }
224 
225     private void possibleDeprecationWarning(java.lang.reflect.Member m) {
226         if (org.callbackparams.CallbackControlPanel.class == paramClass) {
227             System.err.println(m + org.callbackparams.CallbackControlPanel
228                     .DEPRECATION_MESSAGE);
229         }
230         CallbackFieldWarning.possibleDeprecationWarning(m);
231     }
232 
233     Object asCallback(Object callbackRecordElement) {
234 
235         if (isCompatibleCallback(callbackRecordElement)) {
236             return callbackRecordElement;
237         }
238 
239         if (callbackRecordElement instanceof org.callbackparams.CallbackFactory) {
240             Object suppliedCallback =
241                     ((org.callbackparams.CallbackFactory)callbackRecordElement).getCallback();
242             if (isCompatibleCallback(suppliedCallback)) {
243                 return suppliedCallback;
244             }
245         }
246 
247         Object callback = annotationSpecified.asCallback(callbackRecordElement);
248         if (null != callback) {
249             return callback;
250         }
251 
252         /* Using secondary CallbackFactory is the last resort ... */
253         return this.secondaryFactory.asCallback(callbackRecordElement);
254     }
255 
256     boolean isCompatibleCallback(Object possibleCallback) {
257         return this.paramClass.isInstance(possibleCallback);
258     }
259 
260     Class getParamClass() {
261         return paramClass;
262     }
263 }