View Javadoc

1   /*
2    * Copyright 2002-2004 The Apache Software Foundation
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  package org.apache.commons.clazz.reflect;
17  
18  import java.lang.reflect.Constructor;
19  
20  import org.apache.commons.clazz.Clazz;
21  import org.apache.commons.clazz.ClazzAccessException;
22  import org.apache.commons.clazz.ClazzLoader;
23  import org.apache.commons.clazz.ModelClazzLoader;
24  
25  /**
26   * 
27   * @author <a href="mailto:dmitri@apache.org">Dmitri Plotnikov</a>
28   * @version $Id: ReflectedClazzLoader.java 155436 2005-02-26 13:17:48Z dirkv $
29   */
30  public abstract class ReflectedClazzLoader extends ClazzLoader {
31  
32      private ClassLoader classLoader;
33      
34      public ReflectedClazzLoader(
35              ModelClazzLoader modelClazzLoader,
36              ClassLoader classLoader) 
37      {
38          super(modelClazzLoader);
39          this.classLoader = classLoader;
40      }
41      
42      public String getClazzName(Object instance) {
43          if (instance == null) {
44              return null;
45          }
46          return Clazz.getCanonicalClassName(instance.getClass()); 
47      }
48      
49      public Clazz getClazzForName(String name) {
50          Class javaClass = null;
51          try {
52              if (classLoader != null) {
53                  javaClass = classLoader.loadClass(name);
54              }
55              else {
56                  javaClass = Class.forName(name);
57              }
58          }
59          catch (ClassNotFoundException ex) {
60              // Won't happen
61          }
62  
63          if (javaClass == null) {
64              try {
65                  javaClass = getClazzForCanonicalName(classLoader, name);
66              }
67              catch (ClassNotFoundException ex) {
68                  // Won't happen
69              }
70          }
71          if (javaClass == null) {
72              return null;
73          }
74          
75          if (javaClass.isArray()) {
76              // TBD: automatically produce clazzes for arrays
77          }
78          else {
79              Clazz clazz = lookupCustomClazz(javaClass);
80              if (clazz != null) {
81                  return clazz;
82              }
83          }
84          if (!isSupportedClass(javaClass)) {
85              return null;
86          }
87          return createClazz(javaClass);
88      }
89      
90      /**
91       * Try to find and allocate a custom Clazz for the specified Java class.
92       * Build the name of the custom Clazz class out of the name of the 
93       * Java class, followed by the model name, followed by the word "Clazz".
94       * For example, if the java class is called "my.Foo", and the model is
95       * "Standard", the corresponding Clazz class name is "my.FooStandardClazz".
96       * 
97       * @param javaClass
98       * @return Clazz
99       */
100     protected Clazz lookupCustomClazz(Class javaClass) {
101         if (javaClass.isPrimitive()) {
102             return null;
103         }
104         
105         String customClazzName = javaClass.getName() + getModel() + "Clazz";
106 
107         Class customClazzClass;
108         try {
109             if (classLoader == null) {
110                 classLoader = getClass().getClassLoader();
111             }
112             if (classLoader != null) {
113                 customClazzClass = classLoader.loadClass(customClazzName);
114             }
115             else {
116                 customClazzClass = Class.forName(customClazzName);
117             }
118         }
119         catch (ClassNotFoundException e) {
120             return null;
121         }
122             
123         if (!Clazz.class.isAssignableFrom(customClazzClass)) {
124             return null;
125         }
126         
127         Constructor constructor;
128         try {
129             constructor =
130                 customClazzClass.getConstructor(
131                     new Class[] { ClazzLoader.class, Class.class });
132             if (constructor == null) {
133                 return null;
134             }
135         }
136         catch (NoSuchMethodException e) {
137             return null;
138         }
139                 
140         try {
141             return (Clazz) constructor.newInstance(
142                 new Object[] { getModelClazzLoader(), javaClass });
143         }
144         catch (Exception e) {
145             throw new ClazzAccessException(
146                 "Cannot instantiate custom ReflectedClazz " + customClazzName,
147                 e);
148         }
149     }
150         
151 
152     /**
153      * Converts a canonical class name into the corresponding internal JVM type
154      * name format and looks up the type.
155      */
156     private static Class getClazzForCanonicalName(
157             ClassLoader classLoader,
158             String name)
159         throws ClassNotFoundException 
160     {
161         int arrayDepth = 0;
162         while (name.endsWith("[]")) {
163             arrayDepth++;
164             name = name.substring(0, name.length() - 2);
165         }
166 
167         if (name.equals("boolean")) {
168             return getPrimitiveType(classLoader, arrayDepth, Boolean.TYPE, 'Z');
169         }
170         else if (name.equals("byte")) {
171             return getPrimitiveType(classLoader, arrayDepth, Byte.TYPE, 'B');
172         }
173         else if (name.equals("char")) {
174             return 
175                 getPrimitiveType(classLoader, arrayDepth, Character.TYPE, 'C');
176         }
177         else if (name.equals("short")) {
178             return getPrimitiveType(classLoader, arrayDepth, Short.TYPE, 'S');
179         }
180         else if (name.equals("int")) {
181             return getPrimitiveType(classLoader, arrayDepth, Integer.TYPE, 'I');
182         }
183         else if (name.equals("long")) {
184             return getPrimitiveType(classLoader, arrayDepth, Long.TYPE, 'J');
185         }
186         else if (name.equals("float")) {
187             return getPrimitiveType(classLoader, arrayDepth, Float.TYPE, 'F');
188         }
189         else if (name.equals("double")) {
190             return getPrimitiveType(classLoader, arrayDepth, Double.TYPE, 'D');
191         }
192         if (arrayDepth != 0) {
193             StringBuffer buffer = new StringBuffer();
194             for (int i = 0; i < arrayDepth; i++) {
195                 buffer.append('[');
196             }
197             buffer.append('L');
198             buffer.append(name);
199             buffer.append(';');
200             name = buffer.toString();
201         }
202 
203         if (classLoader != null) {
204             return classLoader.loadClass(name);
205         }
206         else {
207             return Class.forName(name);
208         }
209     }
210 
211     /**
212      * Hacking around some JVM quirks, looks up Classes for primitive types and
213      * arrays thereof.
214      */
215     private static Class getPrimitiveType(
216             ClassLoader classLoader,
217             int arrayDepth,
218             Class primitiveType,
219             char typeLetter)
220         throws ClassNotFoundException 
221     {
222         if (arrayDepth == 0) {
223             return primitiveType;
224         }
225 
226         StringBuffer buffer = new StringBuffer();
227         for (int i = 0; i < arrayDepth; i++) {
228             buffer.append('[');
229         }
230         buffer.append(typeLetter);
231         String name = buffer.toString();
232 
233         if (classLoader != null) {
234             return classLoader.loadClass(name);
235         }
236         else {
237             return Class.forName(name);
238         }
239     }      
240     
241     /**
242      * Returns <code>true</code> for all objects.
243      * 
244      * @see ClazzLoader#isMember(Object)
245      */
246     public boolean isMember(Object instance) {
247         if (instance == null) {
248             return false;
249         }
250         return isSupportedClass(instance.getClass());
251     }
252 
253     /**
254      * Returns true for all classes supported by this specific clazz loader.
255      */
256     protected boolean isSupportedClass(Class javaClass) {
257         return true;
258     }
259 
260     /**
261      * Override this method to construct an Clazz for the given Class
262      * (javaClass). Make sure that the new Clazz is initialized with the
263      * supplied clazzLoader representing the clazz loader group, not with
264      * <code>this</code>.
265      */
266     protected abstract Clazz createClazz(Class javaClass);
267 
268     /**
269      * @see ClazzLoader#defineClazz(String, Class, Class)
270      */
271     public Clazz defineClazz(
272                 String name,
273                 Class clazzClass,
274                 Class instanceClass) 
275     {
276         return null;
277     }
278 }