View Javadoc

1   /*
2    * Copyright 2010-2011 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.support;
18  
19  import java.lang.reflect.Constructor;
20  import java.lang.reflect.InvocationTargetException;
21  import java.util.Arrays;
22  import java.util.HashMap;
23  import java.util.Map;
24  
25  /**
26   * Serves as a factory for creating instances of its wrapped class. It does not
27   * really do much except hiding the checked exceptions that would otherwise
28   * have to be taken care of.
29   * 
30   * @author Henrik Kaipe
31   */
32  public class ClassWrapper {
33  
34      private Class wrappedClass;
35      
36      /**
37       * Serves as a cache for single-argument constructors.
38       */
39      private Map constructors = new HashMap();
40  
41      public ClassWrapper(Class wrappedClass) {
42          this.wrappedClass = wrappedClass;
43      }
44  
45      public Class getWrappedClass() {
46          return wrappedClass;
47      }
48      
49      /**
50       * Returns a new instance, which is created with the default constructor of
51       * the wrapped class.
52       */
53      public Object newInstance() {
54          try {
55              Constructor constr = wrappedClass.getDeclaredConstructor(null);
56              try {
57                  constr.setAccessible(true);
58              } catch(SecurityException neverMind) {/*Try anyway*/}
59              return constr.newInstance(null);
60          } catch (Exception x) {
61              throw ExceptionUtil.unchecked(x);
62          }
63      }
64      
65      /**
66       * Returns a new instance of the wrapped class. It is created with the first
67       * constructor found that accepts the specified constructor argument.
68       */
69      public Object newInstance(Object singleConstructorArgument) {
70          final Constructor constr;
71          if (null == singleConstructorArgument) {
72              constr = chooseNullConstructor();
73              if (0 == constr.getParameterTypes().length) {
74                  return newInstance();
75              }
76          } else {
77              constr = chooseConstructor(singleConstructorArgument.getClass());
78          }
79          try {
80              return constr.newInstance(new Object[] {singleConstructorArgument});
81          } catch (Exception x) {
82              throw new Error(x);
83          }
84      }
85  
86      public Object newInstance(final Object[] multiargConstructorArguments) {
87          final Constructor[] constructors = getWrappedClass().getConstructors();
88          for (int i = 0 ; i < constructors.length ; ++i) {
89              if (multiargConstructorArguments.length
90                      != constructors[i].getParameterTypes().length) {
91                  continue;
92              }
93              try {
94                  return constructors[i]
95                          .newInstance(multiargConstructorArguments);
96              } catch (InstantiationException couldTheClassBeAbstract) {
97                  throw new Error(couldTheClassBeAbstract);
98              } catch (IllegalAccessException shouldNeverHappen) {
99                  throw new Error(shouldNeverHappen);
100             } catch (IllegalArgumentException unsuiteableConstructor) {
101                 continue;
102             } catch (InvocationTargetException ex) {
103                 throwUnchecked(ex);
104             }
105         }
106         throw new Error("Could not find any suiteable constructor for arguments"
107                 + Arrays.asList(multiargConstructorArguments));
108     }
109 
110     private Constructor chooseNullConstructor() {
111         Constructor constr = (Constructor) this.constructors.get(null);
112         if (null != constr) {
113             return constr;
114             
115         } else {
116             try {
117                 constr = getWrappedClass().getConstructor(new Class[]{});
118             } catch (NoSuchMethodException x) {
119                 final Constructor[] allConstructors =
120                         getWrappedClass().getConstructors();
121                 for (int i = 0 ; i < allConstructors.length ; ++i) {
122                     final Class[] paramTypes =
123                             allConstructors[i].getParameterTypes();
124                     if (1 == paramTypes.length
125                             && false == paramTypes[0].isPrimitive()) {
126                         constr = allConstructors[0];
127                         break;
128                     }
129                 }
130             }
131             if (null == constr) {
132                 throw new NoSuchMethodError("Cannot find any constructor that"
133                         + " accepts as null as argument");
134             } else {
135                 this.constructors.put(null, constr);
136                 return constr;
137             }
138         }
139         
140     }
141     
142     private Constructor chooseConstructor(Class argumentClass) {
143         Constructor constr = (Constructor) this.constructors.get(argumentClass);
144         if (null != constr) {
145             return constr;
146             
147         } else {
148             constr = lookupExactOrPrimitiveConstructor(argumentClass);
149             if (null == constr) {
150                 final Constructor[] allConstructors =
151                         getWrappedClass().getConstructors();
152                 for (int i = 0 ; i < allConstructors.length ; ++i) {
153                     final Class[] paramTypes =
154                             allConstructors[i].getParameterTypes();
155                     if (1 == paramTypes.length
156                             && paramTypes[0].isAssignableFrom(argumentClass)) {
157                         constr = allConstructors[i];
158                         break;
159                     }
160                 }
161             }
162             if (null == constr) {
163                 throw new NoSuchMethodError("Cannot find any constructor that"
164                         + " accepts a single " + argumentClass + " argument.");
165             } else {
166                 this.constructors.put(argumentClass, constr);
167                 return constr;
168             }
169         }
170     }
171 
172     private Constructor lookupExactOrPrimitiveConstructor(Class argumentClass) {
173         Class[] paramClassArray = {argumentClass};
174         try {
175             return getWrappedClass().getConstructor(paramClassArray);
176         } catch (NoSuchMethodException x) {
177             if (Integer.class == argumentClass) {
178                 paramClassArray[0] = int.class;
179             } else if (Boolean.class == argumentClass) {
180                 paramClassArray[0] = boolean.class;
181             } else if (Character.class == argumentClass) {
182                 paramClassArray[0] = char.class;
183             } else if (Byte.class == argumentClass) {
184                 paramClassArray[0] = byte.class;
185             } else if (Long.class == argumentClass) {
186                 paramClassArray[0] = long.class;
187             } else if (Double.class == argumentClass) {
188                 paramClassArray[0] = double.class;
189             } else if (Float.class == argumentClass) {
190                 paramClassArray[0] = float.class;
191             } else if (Short.class == argumentClass) {
192                 paramClassArray[0] = short.class;
193             } else {
194                 return null;
195             }
196             try {
197                 return getWrappedClass().getConstructor(paramClassArray);
198             } catch (NoSuchMethodException ex) {
199                 return null;
200             }
201         }
202     }
203 
204     private void throwUnchecked(InvocationTargetException x)
205     throws RuntimeException, Error {
206         Throwable t = x.getTargetException();
207         if (t instanceof RuntimeException) {
208             throw (RuntimeException)t;
209         } else if (t instanceof Error) {
210             throw (Error)t;
211         } else {
212             throw new Error(t);
213         }
214     }
215 }