001    /*
002     * Copyright 2002-2004 The Apache Software Foundation
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.apache.commons.clazz.reflect;
017    
018    import java.lang.reflect.Constructor;
019    
020    import org.apache.commons.clazz.Clazz;
021    import org.apache.commons.clazz.ClazzAccessException;
022    import org.apache.commons.clazz.ClazzLoader;
023    import org.apache.commons.clazz.ModelClazzLoader;
024    
025    /**
026     * 
027     * @author <a href="mailto:dmitri@apache.org">Dmitri Plotnikov</a>
028     * @version $Id: ReflectedClazzLoader.java 155436 2005-02-26 13:17:48Z dirkv $
029     */
030    public abstract class ReflectedClazzLoader extends ClazzLoader {
031    
032        private ClassLoader classLoader;
033        
034        public ReflectedClazzLoader(
035                ModelClazzLoader modelClazzLoader,
036                ClassLoader classLoader) 
037        {
038            super(modelClazzLoader);
039            this.classLoader = classLoader;
040        }
041        
042        public String getClazzName(Object instance) {
043            if (instance == null) {
044                return null;
045            }
046            return Clazz.getCanonicalClassName(instance.getClass()); 
047        }
048        
049        public Clazz getClazzForName(String name) {
050            Class javaClass = null;
051            try {
052                if (classLoader != null) {
053                    javaClass = classLoader.loadClass(name);
054                }
055                else {
056                    javaClass = Class.forName(name);
057                }
058            }
059            catch (ClassNotFoundException ex) {
060                // Won't happen
061            }
062    
063            if (javaClass == null) {
064                try {
065                    javaClass = getClazzForCanonicalName(classLoader, name);
066                }
067                catch (ClassNotFoundException ex) {
068                    // Won't happen
069                }
070            }
071            if (javaClass == null) {
072                return null;
073            }
074            
075            if (javaClass.isArray()) {
076                // TBD: automatically produce clazzes for arrays
077            }
078            else {
079                Clazz clazz = lookupCustomClazz(javaClass);
080                if (clazz != null) {
081                    return clazz;
082                }
083            }
084            if (!isSupportedClass(javaClass)) {
085                return null;
086            }
087            return createClazz(javaClass);
088        }
089        
090        /**
091         * Try to find and allocate a custom Clazz for the specified Java class.
092         * Build the name of the custom Clazz class out of the name of the 
093         * Java class, followed by the model name, followed by the word "Clazz".
094         * For example, if the java class is called "my.Foo", and the model is
095         * "Standard", the corresponding Clazz class name is "my.FooStandardClazz".
096         * 
097         * @param javaClass
098         * @return Clazz
099         */
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    }