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    }