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 }