001 package org.mesopotamia.lang.java.ref.bcel; 002 003 import java.lang.reflect.Modifier; 004 import java.util.ArrayList; 005 import java.util.List; 006 import java.util.logging.Logger; 007 008 import org.apache.bcel.classfile.Field; 009 import org.apache.bcel.classfile.JavaClass; 010 import org.apache.bcel.classfile.Method; 011 import org.apache.bcel.generic.Type; 012 import org.mesopotamia.lang.java.BuiltInType; 013 import org.mesopotamia.lang.java.Null; 014 import org.mesopotamia.lang.java.TypeDefinition; 015 import org.mesopotamia.lang.java.TypeSpecification; 016 import org.mesopotamia.lang.java.TypeDefinition.ParameterMatch; 017 import org.mesopotamia.lang.java.ref.MethodInfo; 018 import org.mesopotamia.lang.java.ref.Scope; 019 import org.mesopotamia.lang.java.ref.TypeInfo; 020 import org.mesopotamia.lang.java.ref.TypeSpecInfo; 021 import org.mesopotamia.lang.java.ref.VariableInfo; 022 023 public class JavaClassTypeInfo implements TypeInfo { 024 025 private static final String JAVA_LANG_OBJECT = "java.lang.Object"; 026 027 private static final Logger logger = Logger.getLogger(JavaClassTypeInfo.class.getName()); 028 029 private JavaClass javaClass; 030 031 private Scope scope; 032 033 private JavaClassTypeInfoRepository repository; 034 035 JavaClassTypeInfo(JavaClass javaClass, Scope scope, JavaClassTypeInfoRepository repository) { 036 this.javaClass = javaClass; 037 this.scope = scope; 038 this.repository = repository; 039 } 040 041 public TypeInfo findNestedType(String name) { 042 String nName = javaClass.getClassName()+"$"+name; 043 TypeInfo ret = repository.getTypeInfo(nName, scope); 044 if (ret!=null) { 045 return ret; 046 } 047 048 if (JAVA_LANG_OBJECT.equals(javaClass.getClassName())) { 049 return null; 050 } 051 052 String superClassName = javaClass.getSuperclassName(); 053 TypeInfo superInfo = repository.getTypeInfo(superClassName, scope); 054 if (superInfo!=null) { 055 TypeInfo sret = superInfo.findNestedType(name); 056 if (sret!=null) { 057 return sret; 058 } 059 } 060 061 for (String interfaceName: javaClass.getInterfaceNames()) { 062 TypeInfo iInfo = repository.getTypeInfo(interfaceName, scope); 063 if (iInfo!=null) { 064 TypeInfo iret = iInfo.findNestedType(name); 065 if (iret!=null) { 066 return iret; 067 } 068 } 069 } 070 071 return null; 072 } 073 074 public MethodInfo findTypeMethod(String name, String[] parameterTypes) { 075 List<TypeSpecInfo> pTypes=new ArrayList<TypeSpecInfo>(); 076 boolean pTypesResolved = true; 077 for (int i=0; parameterTypes!=null && i<parameterTypes.length; ++i) { 078 if (parameterTypes[i]==null) { 079 StringBuffer sig = new StringBuffer(); 080 for (String pt: parameterTypes) { 081 sig.append(sig.length()==0 ? "(" : ", "); 082 sig.append(pt); 083 } 084 sig.append(")"); 085 logger.warning("Could not resolve method "+name+sig.toString()+" argument "+i+" type ["+javaClass.getClassName()+"]"); 086 pTypes.add(null); 087 pTypesResolved = false; 088 } else { 089 pTypes.add(string2TypeSpecInfo(parameterTypes[i], scope, repository)); 090 } 091 } 092 093 MethodInfo candidate = null; 094 MethodInfo pnumMatch = null; 095 Method[] ma = javaClass.getMethods(); 096 for (int i=0; i<ma.length; ++i) { 097 if (!ma[i].getName().equals(name)) { 098 continue; 099 } 100 101 Type[] at = ma[i].getArgumentTypes(); 102 if ((parameterTypes==null || parameterTypes.length==0) && (at==null || at.length==0)) { 103 return new JavaClassMethodInfo(ma[i], scope, repository, this); 104 } 105 106 int pl = parameterTypes==null ? 0 : parameterTypes.length; 107 int al = at==null ? 0 : at.length; 108 if (pl!=al) { 109 continue; 110 } 111 112 List<TypeSpecInfo> mTypes = new ArrayList<TypeSpecInfo>(); 113 for (int j=0; j<at.length; ++j) { 114 mTypes.add(type2TypeSpecInfo(at[j], scope, repository)); 115 } 116 ParameterMatch matchResult = TypeDefinition.match(mTypes, pTypes); 117 if (matchResult == ParameterMatch.FULL_MATCH) { 118 return new JavaClassMethodInfo(ma[i], scope, repository, this); 119 } else if (matchResult == ParameterMatch.MATCH) { 120 candidate = TypeDefinition.selectMoreSpecific(candidate, new JavaClassMethodInfo(ma[i], scope, repository, this)); 121 } else if (!pTypesResolved && pnumMatch==null) { 122 pnumMatch = new JavaClassMethodInfo(ma[i], scope, repository, this); 123 } 124 } 125 126 if (candidate==null && !pTypesResolved && pnumMatch!=null) { 127 return pnumMatch; 128 } 129 130 if (JAVA_LANG_OBJECT.equals(javaClass.getClassName())) { 131 return candidate; 132 } 133 134 String superClassName = javaClass.getSuperclassName(); 135 TypeInfo superInfo = repository.getTypeInfo(superClassName, scope); 136 if (superInfo!=null) { 137 candidate = TypeDefinition.selectMoreSpecific(candidate, superInfo.findTypeMethod(name, parameterTypes)); 138 } 139 for (String interfaceName: javaClass.getInterfaceNames()) { 140 TypeInfo iInfo = repository.getTypeInfo(interfaceName, scope); 141 if (iInfo!=null) { 142 candidate = TypeDefinition.selectMoreSpecific(candidate, iInfo.findTypeMethod(name, parameterTypes)); 143 } 144 } 145 146 return candidate; 147 } 148 149 public MethodInfo findConstructor(String[] parameterTypes) { 150 List<TypeSpecInfo> pTypes=new ArrayList<TypeSpecInfo>(); 151 boolean pTypesResolved = true; 152 for (int i=0; parameterTypes!=null && i<parameterTypes.length; ++i) { 153 if (parameterTypes[i]==null) { 154 StringBuffer sig = new StringBuffer(); 155 for (String pt: parameterTypes) { 156 sig.append(sig.length()==0 ? "(" : ", "); 157 sig.append(pt); 158 } 159 sig.append(")"); 160 logger.warning("Could not resolve constructor <init>"+sig.toString()+" argument "+i+" type ["+javaClass.getClassName()+"]"); 161 pTypes.add(null); 162 pTypesResolved = false; 163 } else { 164 pTypes.add(string2TypeSpecInfo(parameterTypes[i], scope, repository)); 165 } 166 } 167 168 MethodInfo candidate = null; 169 MethodInfo pnumMatch = null; 170 Method[] ma = javaClass.getMethods(); 171 for (int i=0; i<ma.length; ++i) { 172 if (!ma[i].getName().equals(MethodInfo.CONSTRUCTOR_NAME)) { 173 continue; 174 } 175 176 Type[] at = ma[i].getArgumentTypes(); 177 List<TypeSpecInfo> mTypes = new ArrayList<TypeSpecInfo>(); 178 for (int j=0; j<at.length; ++j) { 179 mTypes.add(type2TypeSpecInfo(at[j], scope, repository)); 180 } 181 ParameterMatch matchResult = TypeDefinition.match(mTypes, pTypes); 182 if (matchResult == ParameterMatch.FULL_MATCH) { 183 return new JavaClassMethodInfo(ma[i], scope, repository, this); 184 } else if (matchResult == ParameterMatch.MATCH) { 185 candidate = TypeDefinition.selectMoreSpecific(candidate, new JavaClassMethodInfo(ma[i], scope, repository, this)); 186 } else if (!pTypesResolved && pnumMatch==null) { 187 pnumMatch = new JavaClassMethodInfo(ma[i], scope, repository, this); 188 } 189 } 190 191 return candidate==null ? pnumMatch : candidate; 192 } 193 194 public VariableInfo findTypeVariable(String name) { 195 Field[] fields = javaClass.getFields(); 196 for (int i=0; i<fields.length; ++i) { 197 if (fields[i].getName().equals(name)) { 198 return new JavaClassVariableInfo(fields[i], scope, repository, this); 199 } 200 } 201 202 if (JAVA_LANG_OBJECT.equals(javaClass.getClassName())) { 203 return null; 204 } 205 206 String superClassName = javaClass.getSuperclassName(); 207 TypeInfo superInfo = repository.getTypeInfo(superClassName, scope); 208 if (superInfo!=null) { 209 VariableInfo ret = superInfo.findTypeVariable(name); 210 if (ret!=null) { 211 return ret; 212 } 213 } 214 for (String interfaceName: javaClass.getInterfaceNames()) { 215 TypeInfo iInfo = repository.getTypeInfo(interfaceName, scope); 216 if (iInfo!=null) { 217 VariableInfo ret = iInfo.findTypeVariable(name); 218 if (ret!=null) { 219 return ret; 220 } 221 } 222 } 223 224 return null; 225 } 226 227 public boolean isKindOf(String superFcn) { 228 if (javaClass.getClassName().replace('$', '.').equals(superFcn)) { // Decide on $ or . in class names. 229 return true; 230 } 231 232 if (JAVA_LANG_OBJECT.equals(javaClass.getClassName())) { 233 return false; 234 } 235 236 String superClassName = javaClass.getSuperclassName(); 237 TypeInfo superInfo = repository.getTypeInfo(superClassName, scope); 238 if (superInfo!=null && superInfo.isKindOf(superFcn)) { 239 return true; 240 } 241 for (String interfaceName: javaClass.getInterfaceNames()) { 242 TypeInfo iInfo = repository.getTypeInfo(interfaceName, scope); 243 if (iInfo!=null && iInfo.isKindOf(superFcn)) { 244 return true; 245 } 246 } 247 248 return false; 249 } 250 251 public TypeInfo getDeclaringType() { 252 String name = javaClass.getClassName(); 253 int idx = name.indexOf("$"); 254 if (idx==-1) { 255 return null; 256 } 257 return repository.getTypeInfo(name.substring(0, idx), scope); 258 } 259 260 public String getFcn() { 261 return javaClass.getClassName().replace('$', '.'); 262 } 263 264 public List<String> getModifiers() { 265 return modifiersList(javaClass.getModifiers()); 266 } 267 268 public String getName() { 269 String ret = javaClass.getClassName(); 270 int idx = ret.lastIndexOf('.'); 271 return idx == -1 ? ret : ret.substring(idx+1); 272 } 273 274 public static List<String> modifiersList(int mod) { 275 List<String> ret = new ArrayList<String>(); 276 277 if ((mod & Modifier.PUBLIC) != 0) { 278 ret.add("public "); 279 } 280 if ((mod & Modifier.PROTECTED) != 0) { 281 ret.add("protected "); 282 } 283 if ((mod & Modifier.PRIVATE) != 0) { 284 ret.add("private "); 285 } 286 287 /* Canonical order */ 288 if ((mod & Modifier.ABSTRACT) != 0) { 289 ret.add("abstract "); 290 } 291 if ((mod & Modifier.STATIC) != 0) { 292 ret.add("static "); 293 } 294 if ((mod & Modifier.FINAL) != 0) { 295 ret.add("final "); 296 } 297 if ((mod & Modifier.TRANSIENT) != 0) { 298 ret.add("transient "); 299 } 300 if ((mod & Modifier.VOLATILE) != 0) { 301 ret.add("volatile "); 302 } 303 if ((mod & Modifier.SYNCHRONIZED) != 0) { 304 ret.add("synchronized "); 305 } 306 if ((mod & Modifier.NATIVE) != 0) { 307 ret.add("native "); 308 } 309 if ((mod & Modifier.STRICT) != 0) { 310 ret.add("strictfp "); 311 } 312 if ((mod & Modifier.INTERFACE) != 0) { 313 ret.add("interface "); 314 } 315 316 return ret; 317 } 318 319 /** 320 * 321 * @param type 322 * @param repository 323 */ 324 public static TypeSpecInfo type2TypeSpecInfo(Type type, Scope scope, JavaClassTypeInfoRepository repository) { 325 return string2TypeSpecInfo(type.toString(), scope, repository); 326 } 327 328 public static TypeSpecInfo string2TypeSpecInfo(String typeName, Scope scope, JavaClassTypeInfoRepository repository) { 329 if (typeName==null) { 330 return null; 331 } 332 333 if (Null.NULL_FCN.equals(typeName)) { 334 return Null.NULL_TYPE_SPEC_INFO; 335 } 336 337 int dimensions=0; 338 while (typeName.endsWith(TypeSpecInfo.ARRAY_BRACKETS)) { 339 ++dimensions; 340 typeName=typeName.substring(0, typeName.length()-TypeSpecInfo.ARRAY_BRACKETS.length()); 341 } 342 343 TypeInfo ti = BuiltInType.getBuiltInType(typeName); 344 if (ti==null && scope!=null) { 345 ti = scope.findType(typeName); 346 } 347 348 if (ti==null) { 349 ti = repository.getTypeInfo(typeName, scope); 350 } 351 352 if (ti==null) { 353 return null; 354 } 355 356 final int d = dimensions; 357 final TypeInfo fti = ti; 358 return new TypeSpecInfo() { 359 360 public int getDimensions() { 361 return d; 362 } 363 364 public TypeInfo getTypeInfo() { 365 return fti; 366 } 367 368 @Override 369 public String toString() { 370 return TypeSpecification.toString(this); 371 } 372 373 }; 374 375 } 376 377 @Override 378 public String toString() { 379 return javaClass.getClassName()+" ["+getClass().getName()+"]"; 380 } 381 }