001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021package org.biojava.utils.bytecode; 022 023import java.util.*; 024import java.lang.reflect.*; 025 026// fix for array types so that their descriptors are not prefixed with L 027 028/** 029 * CodeClass instances that represent normal Java Class objects. 030 * 031 * <p> 032 * Instances of IntrospectedCodeClass are generated using the static factory 033 * methods named forClass(). These methods ensure that the same 034 * IntrospectedCodeClass instance is returned for multiple invocations with 035 * the same argument. 036 * </p> 037 * 038 * @author Thomas Down 039 * @author Matthew Pocock 040 */ 041public class IntrospectedCodeClass implements CodeClass { 042 private static Map introspectedClasses; 043 private static Map primitiveDescriptors; 044 045 static { 046 introspectedClasses = new HashMap(); 047 048 primitiveDescriptors = new HashMap(); 049 primitiveDescriptors.put(Byte.TYPE, "B"); 050 primitiveDescriptors.put(Character.TYPE, "C"); 051 primitiveDescriptors.put(Double.TYPE, "D"); 052 primitiveDescriptors.put(Float.TYPE, "F"); 053 primitiveDescriptors.put(Integer.TYPE, "I"); 054 primitiveDescriptors.put(Long.TYPE, "J"); 055 primitiveDescriptors.put(Short.TYPE, "S"); 056 primitiveDescriptors.put(Boolean.TYPE, "Z"); 057 primitiveDescriptors.put(Void.TYPE, "V"); 058 059 } 060 061 /** 062 * Get the CodeClass for a Java Class. 063 * 064 * @param c the Java Class to reflect 065 * @return a CodeClass representing the class 066 */ 067 public static CodeClass forClass(Class c) { 068 CodeClass cc = (CodeClass) introspectedClasses.get(c); 069 if (cc == null) { 070 cc = new IntrospectedCodeClass(c); 071 introspectedClasses.put(c, cc); 072 } 073 return cc; 074 } 075 076 /** 077 * Get the CodeClass for a Java class name. 078 * 079 * @param name the Java class name to reflect 080 * @return a CodeClass representing the class 081 */ 082 public static CodeClass forClass(String name) throws ClassNotFoundException { 083 Class c = ClassLoader.getSystemClassLoader().loadClass(name); 084 085 return forClass(c); 086 } 087 088 public static CodeMethod forMethod(Method method) { 089 return new IntrospectedCodeMethod(method); 090 } 091 092 // 093 // Instance 094 // 095 096 private Class clazz; 097 098 private IntrospectedCodeClass(Class c) { 099 this.clazz = c; 100 } 101 102 public String getName() { 103 return clazz.getName(); 104 } 105 106 public String getJName() { 107 String name = getName(); 108 StringBuffer sb = new StringBuffer(); 109 for (int i = 0; i < name.length(); ++i) { 110 char c = name.charAt(i); 111 if (c == '.') 112 sb.append('/'); 113 else 114 sb.append(c); 115 } 116 117 return sb.toString(); 118 } 119 120 public String getDescriptor() { 121 if (clazz.isPrimitive()) { 122 String desc = (String) primitiveDescriptors.get(clazz); 123 if (desc == null) { 124 throw new RuntimeException("Unknown primitive type " + clazz.getName() + ", eeek!"); 125 } 126 return desc; 127 } 128 129 if (clazz.isArray()) { 130 return "[" + IntrospectedCodeClass.forClass(clazz.getComponentType()).getDescriptor(); 131 } else { 132 String name = getName(); 133 StringBuffer sb = new StringBuffer(); 134 sb.append('L'); 135 for (int i = 0; i < name.length(); ++i) { 136 char c = name.charAt(i); 137 if (c == '.') { 138 sb.append('/'); 139 } else { 140 sb.append(c); 141 } 142 } 143 sb.append(';'); 144 return sb.toString(); 145 } 146 } 147 148 public int getModifiers() { 149 return clazz.getModifiers(); 150 } 151 152 public CodeClass getSuperClass() { 153 return IntrospectedCodeClass.forClass(clazz.getSuperclass()); 154 } 155 156 public List getInterfaces() { 157 Class[] interfaces = clazz.getInterfaces(); 158 return Arrays.asList(interfaces); 159 } 160 161 private Set _methods; 162 private Map _methsByNameSig; 163 private Map _methsByName; 164 165 public Set getMethods() { 166 initMethods(); 167 return _methods; 168 } 169 170 private void initMethods() { 171 if (_methods == null) { 172 Map meths = new HashMap(); 173 popMeths(this.clazz, meths); 174 popIMeths(this.clazz, meths); 175 _methods = new HashSet(meths.values()); 176 _methsByNameSig = new HashMap(); 177 _methsByName = new HashMap(); 178 for(Iterator i = _methods.iterator(); i.hasNext(); ) { 179 CodeMethod m = (CodeMethod) i.next(); 180 Set mbn = (Set) _methsByName.get(m.getName()); 181 if(mbn == null) { 182 _methsByName.put(m.getName(), mbn = new HashSet()); 183 } 184 mbn.add(m); 185 _methsByNameSig.put(makeNameSig(m), m); 186 } 187 } 188 } 189 190 private void popMeths(Class clazz, Map meths) { 191 Method[] methods = clazz.getDeclaredMethods(); 192 for(int i = 0; i < methods.length; i++) { 193 Method m = methods[i]; 194 ArrayList sigList = new ArrayList(); 195 sigList.add(m.getName()); 196 sigList.addAll(Arrays.asList(m.getParameterTypes())); 197 if(!meths.containsKey(sigList)) { 198 meths.put(sigList, new IntrospectedCodeMethod(m)); 199 } 200 } 201 202 Class sup = clazz.getSuperclass(); 203 if(sup != null) { 204 popMeths(sup, meths); 205 } 206 } 207 208 private void popIMeths(Class clazz, Map meths) { 209 if(clazz.isInterface()) { 210 Method[] methods = clazz.getDeclaredMethods(); 211 for(int i = 0; i < methods.length; i++) { 212 Method m = methods[i]; 213 ArrayList sigList = new ArrayList(); 214 sigList.add(m.getName()); 215 sigList.addAll(Arrays.asList(m.getParameterTypes())); 216 if(!meths.containsKey(sigList)) { 217 meths.put(sigList, new IntrospectedCodeMethod(m)); 218 } 219 } 220 Class[] interfaces = clazz.getInterfaces(); 221 for(int i = 0; i < interfaces.length; i++) { 222 popIMeths(interfaces[i], meths); 223 } 224 } 225 226 Class sup = clazz.getSuperclass(); 227 if(sup != null) { 228 popIMeths(sup, meths); 229 } 230 } 231 232 private List makeNameSig(CodeMethod m) { 233 List res = new ArrayList(); 234 res.add(m.getName()); 235 236 for(int i = 0; i < m.numParameters(); i++) { 237 res.add(m.getParameterType(i)); 238 } 239 240 return res; 241 } 242 243 public CodeField getFieldByName(String name) 244 throws NoSuchFieldException { 245 try { 246 Field f = clazz.getField(name); 247 return new CodeField(this, 248 name, 249 IntrospectedCodeClass.forClass(f.getType()), 250 f.getModifiers()); 251 } catch (NoSuchFieldException ex) { 252 throw (NoSuchFieldException) new NoSuchFieldException( 253 "Can't find field " + name + 254 " in class " + getName() 255 ).initCause(ex); 256 } 257 } 258 259 private Set _fields; 260 261 public Set getFields() { 262 if(_fields == null) { 263 _fields = new HashSet(); 264 Field[] fields = clazz.getFields(); 265 for(int fi = 0; fi < fields.length; fi++) { 266 Field f = fields[fi]; 267 _fields.add(new CodeField(this, 268 f.getName(), 269 IntrospectedCodeClass.forClass(f.getType()), 270 f.getModifiers())); 271 } 272 273 _fields = Collections.unmodifiableSet(_fields); 274 } 275 276 return _fields; 277 } 278 279 public Set getMethodsByName(String name) { 280 initMethods(); 281 282 Set hits = (Set) _methsByName.get(name); 283 if(hits == null) { 284 return Collections.EMPTY_SET; 285 } else { 286 return hits; 287 } 288 } 289 290 public CodeMethod getMethod(String name, CodeClass[] args) 291 throws NoSuchMethodException 292 { 293 initMethods(); 294 295 List nameSig = new ArrayList(); 296 nameSig.add(name); 297 for(int i = 0; i < args.length; i++) { 298 nameSig.add(args[i]); 299 } 300 301 CodeMethod cm = (CodeMethod) _methsByNameSig.get(nameSig); 302 303 if(cm == null) { 304 throw new NoSuchMethodException( 305 "Could not find method " + getName() + 306 "." + name + 307 "(" + CodeUtils.classListToString(args) + ")"); 308 } 309 310 return cm; 311 } 312 313 314 public CodeMethod getConstructor(CodeClass[] args) 315 throws NoSuchMethodException { 316 try { 317 Class[] argsC = new Class[args.length]; 318 for (int i = 0; i < args.length; i++) { 319 argsC[i] = ((IntrospectedCodeClass) args[i]).clazz; 320 } 321 return new IntrospectedCodeConstructor(clazz.getConstructor(argsC)); 322 } catch (NoSuchMethodException nsme) { 323 throw (NoSuchMethodException) new NoSuchMethodException( 324 "Could not find constructor new " + getName() + 325 "(" + CodeUtils.classListToString(args) + ")" 326 ).initCause(nsme); 327 } 328 } 329 330 public boolean isPrimitive() { 331 return clazz.isPrimitive(); 332 } 333 334 public boolean isArray() { 335 return clazz.isArray(); 336 } 337 338 public String toString() { 339 return this.getClass().getName() + ": " + clazz.getName(); 340 } 341}