| package org.aspectj.apache.bcel.classfile; |
| |
| /* ==================================================================== |
| * The Apache Software License, Version 1.1 |
| * |
| * Copyright (c) 2001 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Apache" and "Apache Software Foundation" and |
| * "Apache BCEL" must not be used to endorse or promote products |
| * derived from this software without prior written permission. For |
| * written permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * "Apache BCEL", nor may "Apache" appear in their name, without |
| * prior written permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| */ |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.DataOutputStream; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Queue; |
| import java.util.StringTokenizer; |
| |
| import org.aspectj.apache.bcel.Constants; |
| import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; |
| import org.aspectj.apache.bcel.classfile.annotation.RuntimeAnnos; |
| import org.aspectj.apache.bcel.generic.Type; |
| import org.aspectj.apache.bcel.util.SyntheticRepository; |
| |
| /** |
| * Represents a Java class, i.e., the data structures, constant pool, fields, methods and commands contained in a Java .class file. |
| * See <a href="ftp://java.sun.com/docs/specs/">JVM specification</a> for details. |
| * |
| * The intent of this class is to represent a parsed or otherwise existing class file. Those interested in programatically |
| * generating classes should see the <a href="../generic/ClassGen.html">ClassGen</a> class. |
| * |
| * @version $Id: JavaClass.java,v 1.22 2009/09/15 19:40:14 aclement Exp $ |
| * @see org.aspectj.apache.bcel.generic.ClassGen |
| * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> |
| */ |
| public class JavaClass extends Modifiers implements Cloneable, Node { |
| |
| private static final String[] NoInterfaceNames = new String[0]; |
| private static final Field[] NoFields = new Field[0]; |
| private static final Method[] NoMethod = new Method[0]; |
| private static final int[] NoInterfaceIndices = new int[0]; |
| private static final Attribute[] NoAttributes = new Attribute[0]; |
| |
| private String fileName; |
| private String packageName; |
| private String sourcefileName; |
| private int classnameIdx; |
| private int superclassnameIdx; |
| private String classname; |
| private String superclassname; |
| private int major, minor; |
| private ConstantPool cpool; |
| private int[] interfaces; |
| private String[] interfacenames; |
| private Field[] fields; |
| private Method[] methods; |
| private Attribute[] attributes; |
| private AnnotationGen[] annotations; |
| |
| private boolean isGeneric = false; |
| private boolean isAnonymous = false; |
| private boolean isNested = false; |
| private boolean computedNestedTypeStatus = false; |
| |
| // Annotations are collected from certain attributes, don't do it more than necessary! |
| private boolean annotationsOutOfDate = true; |
| |
| // state for dealing with generic signature string |
| private String signatureAttributeString = null; |
| private Signature signatureAttribute = null; |
| private boolean searchedForSignatureAttribute = false; |
| |
| /** |
| * In cases where we go ahead and create something, use the default SyntheticRepository, because we don't know any better. |
| */ |
| private transient org.aspectj.apache.bcel.util.Repository repository = null; |
| |
| public JavaClass(int classnameIndex, int superclassnameIndex, String filename, int major, int minor, int access_flags, |
| ConstantPool cpool, int[] interfaces, Field[] fields, Method[] methods, Attribute[] attributes) { |
| if (interfaces == null) { |
| interfaces = NoInterfaceIndices; |
| } |
| |
| this.classnameIdx = classnameIndex; |
| this.superclassnameIdx = superclassnameIndex; |
| this.fileName = filename; |
| this.major = major; |
| this.minor = minor; |
| this.modifiers = access_flags; |
| this.cpool = cpool; |
| this.interfaces = interfaces; |
| this.fields = (fields == null ? NoFields : fields); |
| this.methods = (methods == null ? NoMethod : methods); |
| this.attributes = (attributes == null ? NoAttributes : attributes); |
| annotationsOutOfDate = true; |
| |
| // Get source file name if available |
| SourceFile sfAttribute = AttributeUtils.getSourceFileAttribute(attributes); |
| sourcefileName = sfAttribute == null ? "<Unknown>" : sfAttribute.getSourceFileName(); |
| |
| /* |
| * According to the specification the following entries must be of type `ConstantClass' but we check that anyway via the |
| * `ConstPool.getConstant' method. |
| */ |
| classname = cpool.getConstantString(classnameIndex, Constants.CONSTANT_Class); |
| classname = Utility.compactClassName(classname, false); |
| |
| int index = classname.lastIndexOf('.'); |
| if (index < 0) { |
| packageName = ""; |
| } else { |
| packageName = classname.substring(0, index); |
| } |
| |
| if (superclassnameIndex > 0) { // May be zero -> class is java.lang.Object |
| superclassname = cpool.getConstantString(superclassnameIndex, Constants.CONSTANT_Class); |
| superclassname = Utility.compactClassName(superclassname, false); |
| } else { |
| superclassname = "java.lang.Object"; |
| } |
| |
| if (interfaces.length == 0) { |
| interfacenames = NoInterfaceNames; |
| } else { |
| interfacenames = new String[interfaces.length]; |
| for (int i = 0; i < interfaces.length; i++) { |
| String str = cpool.getConstantString(interfaces[i], Constants.CONSTANT_Class); |
| interfacenames[i] = Utility.compactClassName(str, false); |
| } |
| } |
| } |
| |
| /** |
| * Called by objects that are traversing the nodes of the tree implicitely defined by the contents of a Java class. I.e., the |
| * hierarchy of methods, fields, attributes, etc. spawns a tree of objects. |
| * |
| * @param v Visitor object |
| */ |
| public void accept(ClassVisitor v) { |
| v.visitJavaClass(this); |
| } |
| |
| /** |
| * Dump class to a file. |
| * |
| * @param file Output file |
| * @throws IOException |
| */ |
| public void dump(File file) throws IOException { |
| String parent = file.getParent(); |
| if (parent != null) { |
| File dir = new File(parent); |
| dir.mkdirs(); |
| } |
| dump(new DataOutputStream(new FileOutputStream(file))); |
| } |
| |
| /** |
| * Dump class to a file named file_name. |
| * |
| * @param file_name Output file name |
| * @exception IOException |
| */ |
| public void dump(String file_name) throws IOException { |
| dump(new File(file_name)); |
| } |
| |
| /** |
| * @return class in binary format |
| */ |
| public byte[] getBytes() { |
| ByteArrayOutputStream s = new ByteArrayOutputStream(); |
| DataOutputStream ds = new DataOutputStream(s); |
| |
| try { |
| dump(ds); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } finally { |
| try { |
| ds.close(); |
| } catch (IOException e2) { |
| e2.printStackTrace(); |
| } |
| } |
| |
| return s.toByteArray(); |
| } |
| |
| /** |
| * Dump Java class to output stream in binary format. |
| */ |
| public void dump(OutputStream file) throws IOException { |
| dump(new DataOutputStream(file)); |
| } |
| |
| /** |
| * Dump Java class to output stream in binary format. |
| */ |
| public void dump(DataOutputStream file) throws IOException { |
| file.writeInt(0xcafebabe); |
| file.writeShort(minor); |
| file.writeShort(major); |
| |
| cpool.dump(file); |
| |
| file.writeShort(modifiers); |
| file.writeShort(classnameIdx); |
| file.writeShort(superclassnameIdx); |
| |
| file.writeShort(interfaces.length); |
| for (int i = 0; i < interfaces.length; i++) { |
| file.writeShort(interfaces[i]); |
| } |
| |
| file.writeShort(fields.length); |
| for (int i = 0; i < fields.length; i++) { |
| fields[i].dump(file); |
| } |
| |
| file.writeShort(methods.length); |
| for (int i = 0; i < methods.length; i++) { |
| methods[i].dump(file); |
| } |
| |
| AttributeUtils.writeAttributes(attributes, file); |
| |
| file.close(); |
| } |
| |
| public Attribute[] getAttributes() { |
| return attributes; |
| } |
| |
| public AnnotationGen[] getAnnotations() { |
| if (annotationsOutOfDate) { |
| // Find attributes that contain annotation data |
| List<AnnotationGen> accumulatedAnnotations = new ArrayList<AnnotationGen>(); |
| for (int i = 0; i < attributes.length; i++) { |
| Attribute attribute = attributes[i]; |
| if (attribute instanceof RuntimeAnnos) { |
| RuntimeAnnos runtimeAnnotations = (RuntimeAnnos) attribute; |
| accumulatedAnnotations.addAll(runtimeAnnotations.getAnnotations()); |
| } |
| } |
| annotations = accumulatedAnnotations.toArray(new AnnotationGen[] {}); |
| annotationsOutOfDate = false; |
| } |
| return annotations; |
| } |
| |
| /** |
| * @return Class name. |
| */ |
| public String getClassName() { |
| return classname; |
| } |
| |
| /** |
| * @return Package name. |
| */ |
| public String getPackageName() { |
| return packageName; |
| } |
| |
| public int getClassNameIndex() { |
| return classnameIdx; |
| } |
| |
| public ConstantPool getConstantPool() { |
| return cpool; |
| } |
| |
| /** |
| * @return Fields, i.e., variables of the class. Like the JVM spec mandates for the classfile format, these fields are those |
| * specific to this class, and not those of the superclass or superinterfaces. |
| */ |
| public Field[] getFields() { |
| return fields; |
| } |
| |
| /** |
| * @return File name of class, aka SourceFile attribute value |
| */ |
| public String getFileName() { |
| return fileName; |
| } |
| |
| /** |
| * @return Names of implemented interfaces. |
| */ |
| public String[] getInterfaceNames() { |
| return interfacenames; |
| } |
| |
| /** |
| * @return Indices in constant pool of implemented interfaces. |
| */ |
| public int[] getInterfaceIndices() { |
| return interfaces; |
| } |
| |
| public int getMajor() { |
| return major; |
| } |
| |
| /** |
| * @return Methods of the class. |
| */ |
| public Method[] getMethods() { |
| return methods; |
| } |
| |
| /** |
| * @return A org.aspectj.apache.bcel.classfile.Method corresponding to java.lang.reflect.Method if any |
| */ |
| public Method getMethod(java.lang.reflect.Method m) { |
| for (int i = 0; i < methods.length; i++) { |
| Method method = methods[i]; |
| |
| if (m.getName().equals(method.getName()) && m.getModifiers() == method.getModifiers() |
| && Type.getSignature(m).equals(method.getSignature())) { |
| return method; |
| } |
| } |
| |
| return null; |
| } |
| |
| public Method getMethod(java.lang.reflect.Constructor<?> c) { |
| for (int i = 0; i < methods.length; i++) { |
| Method method = methods[i]; |
| if (method.getName().equals("<init>") && c.getModifiers() == method.getModifiers() |
| && Type.getSignature(c).equals(method.getSignature())) { |
| return method; |
| } |
| } |
| |
| return null; |
| } |
| |
| public Field getField(java.lang.reflect.Field field) { |
| String fieldName = field.getName(); |
| for (Field f : fields) { |
| if (f.getName().equals(fieldName)) { |
| return f; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * @return Minor number of class file version. |
| */ |
| public int getMinor() { |
| return minor; |
| } |
| |
| /** |
| * @return sbsolute path to file where this class was read from |
| */ |
| public String getSourceFileName() { |
| return sourcefileName; |
| } |
| |
| /** |
| * @return Superclass name. |
| */ |
| public String getSuperclassName() { |
| return superclassname; |
| } |
| |
| /** |
| * @return Class name index. |
| */ |
| public int getSuperclassNameIndex() { |
| return superclassnameIdx; |
| } |
| |
| /** |
| * @param attributes . |
| */ |
| public void setAttributes(Attribute[] attributes) { |
| this.attributes = attributes; |
| annotationsOutOfDate = true; |
| } |
| |
| /** |
| * @param class_name . |
| */ |
| public void setClassName(String class_name) { |
| this.classname = class_name; |
| } |
| |
| /** |
| * @param class_name_index . |
| */ |
| public void setClassNameIndex(int class_name_index) { |
| this.classnameIdx = class_name_index; |
| } |
| |
| /** |
| * @param constant_pool . |
| */ |
| public void setConstantPool(ConstantPool constant_pool) { |
| this.cpool = constant_pool; |
| } |
| |
| /** |
| * @param fields . |
| */ |
| public void setFields(Field[] fields) { |
| this.fields = fields; |
| } |
| |
| /** |
| * Set File name of class, aka SourceFile attribute value |
| */ |
| public void setFileName(String file_name) { |
| this.fileName = file_name; |
| } |
| |
| /** |
| * @param interface_names . |
| */ |
| public void setInterfaceNames(String[] interface_names) { |
| this.interfacenames = interface_names; |
| } |
| |
| /** |
| * @param interfaces . |
| */ |
| public void setInterfaces(int[] interfaces) { |
| this.interfaces = interfaces; |
| } |
| |
| public void setMajor(int major) { |
| this.major = major; |
| } |
| |
| public void setMethods(Method[] methods) { |
| this.methods = methods; |
| } |
| |
| public void setMinor(int minor) { |
| this.minor = minor; |
| } |
| |
| /** |
| * Set absolute path to file this class was read from. |
| */ |
| public void setSourceFileName(String source_file_name) { |
| this.sourcefileName = source_file_name; |
| } |
| |
| /** |
| * @param superclass_name . |
| */ |
| public void setSuperclassName(String superclass_name) { |
| this.superclassname = superclass_name; |
| } |
| |
| /** |
| * @param superclass_name_index . |
| */ |
| public void setSuperclassNameIndex(int superclass_name_index) { |
| this.superclassnameIdx = superclass_name_index; |
| } |
| |
| /** |
| * @return String representing class contents. |
| */ |
| @Override |
| public String toString() { |
| String access = Utility.accessToString(modifiers, true); |
| access = access.equals("") ? "" : access + " "; |
| |
| StringBuffer buf = new StringBuffer(access + Utility.classOrInterface(modifiers) + " " + classname + " extends " |
| + Utility.compactClassName(superclassname, false) + '\n'); |
| int size = interfaces.length; |
| |
| if (size > 0) { |
| buf.append("implements\t\t"); |
| |
| for (int i = 0; i < size; i++) { |
| buf.append(interfacenames[i]); |
| if (i < size - 1) { |
| buf.append(", "); |
| } |
| } |
| |
| buf.append('\n'); |
| } |
| |
| buf.append("filename\t\t" + fileName + '\n'); |
| buf.append("compiled from\t\t" + sourcefileName + '\n'); |
| buf.append("compiler version\t" + major + "." + minor + '\n'); |
| buf.append("access flags\t\t" + modifiers + '\n'); |
| buf.append("constant pool\t\t" + cpool.getLength() + " entries\n"); |
| buf.append("ACC_SUPER flag\t\t" + isSuper() + "\n"); |
| |
| if (attributes.length > 0) { |
| buf.append("\nAttribute(s):\n"); |
| for (int i = 0; i < attributes.length; i++) { |
| buf.append(indent(attributes[i])); |
| } |
| } |
| |
| if (annotations != null && annotations.length > 0) { |
| buf.append("\nAnnotation(s):\n"); |
| for (int i = 0; i < annotations.length; i++) { |
| buf.append(indent(annotations[i])); |
| } |
| } |
| |
| if (fields.length > 0) { |
| buf.append("\n" + fields.length + " fields:\n"); |
| for (int i = 0; i < fields.length; i++) { |
| buf.append("\t" + fields[i] + '\n'); |
| } |
| } |
| |
| if (methods.length > 0) { |
| buf.append("\n" + methods.length + " methods:\n"); |
| for (int i = 0; i < methods.length; i++) { |
| buf.append("\t" + methods[i] + '\n'); |
| } |
| } |
| |
| return buf.toString(); |
| } |
| |
| private static final String indent(Object obj) { |
| StringTokenizer tok = new StringTokenizer(obj.toString(), "\n"); |
| StringBuffer buf = new StringBuffer(); |
| |
| while (tok.hasMoreTokens()) { |
| buf.append("\t" + tok.nextToken() + "\n"); |
| } |
| |
| return buf.toString(); |
| } |
| |
| public final boolean isSuper() { |
| return (modifiers & Constants.ACC_SUPER) != 0; |
| } |
| |
| public final boolean isClass() { |
| return (modifiers & Constants.ACC_INTERFACE) == 0; |
| } |
| |
| public final boolean isAnonymous() { |
| computeNestedTypeStatus(); |
| return this.isAnonymous; |
| } |
| |
| public final boolean isNested() { |
| computeNestedTypeStatus(); |
| return this.isNested; |
| } |
| |
| private final void computeNestedTypeStatus() { |
| if (computedNestedTypeStatus) { |
| return; |
| } |
| // Attribute[] attrs = attributes.getAttributes(); |
| for (int i = 0; i < attributes.length; i++) { |
| if (attributes[i] instanceof InnerClasses) { |
| InnerClass[] innerClasses = ((InnerClasses) attributes[i]).getInnerClasses(); |
| for (int j = 0; j < innerClasses.length; j++) { |
| boolean innerClassAttributeRefersToMe = false; |
| String inner_class_name = cpool.getConstantString(innerClasses[j].getInnerClassIndex(), |
| Constants.CONSTANT_Class); |
| inner_class_name = Utility.compactClassName(inner_class_name); |
| if (inner_class_name.equals(getClassName())) { |
| innerClassAttributeRefersToMe = true; |
| } |
| if (innerClassAttributeRefersToMe) { |
| this.isNested = true; |
| if (innerClasses[j].getInnerNameIndex() == 0) { |
| this.isAnonymous = true; |
| } |
| } |
| } |
| } |
| } |
| this.computedNestedTypeStatus = true; |
| } |
| |
| // J5SUPPORT: |
| /** |
| * Returns true if this class represents an annotation, i.e. it was a 'public @interface blahblah' declaration |
| */ |
| public final boolean isAnnotation() { |
| return (modifiers & Constants.ACC_ANNOTATION) != 0; |
| } |
| |
| /** |
| * Returns true if this class represents an enum type |
| */ |
| public final boolean isEnum() { |
| return (modifiers & Constants.ACC_ENUM) != 0; |
| } |
| |
| /********************* New repository functionality *********************/ |
| |
| /** |
| * Gets the ClassRepository which holds its definition. By default this is the same as SyntheticRepository.getInstance(); |
| */ |
| public org.aspectj.apache.bcel.util.Repository getRepository() { |
| if (repository == null) { |
| repository = SyntheticRepository.getInstance(); |
| } |
| return repository; |
| } |
| |
| /** |
| * Sets the ClassRepository which loaded the JavaClass. Should be called immediately after parsing is done. |
| */ |
| public void setRepository(org.aspectj.apache.bcel.util.Repository repository) { |
| this.repository = repository; |
| } |
| |
| /** |
| * Equivalent to runtime "instanceof" operator. |
| * |
| * @return true if this JavaClass is derived from teh super class |
| */ |
| public final boolean instanceOf(JavaClass super_class) { |
| if (this.equals(super_class)) { |
| return true; |
| } |
| |
| JavaClass[] super_classes = getSuperClasses(); |
| |
| for (int i = 0; i < super_classes.length; i++) { |
| if (super_classes[i].equals(super_class)) { |
| return true; |
| } |
| } |
| |
| if (super_class.isInterface()) { |
| return implementationOf(super_class); |
| } |
| |
| return false; |
| } |
| |
| /** |
| * @return true, if clazz is an implementation of interface inter |
| */ |
| public boolean implementationOf(JavaClass inter) { |
| if (!inter.isInterface()) { |
| throw new IllegalArgumentException(inter.getClassName() + " is no interface"); |
| } |
| |
| if (this.equals(inter)) { |
| return true; |
| } |
| |
| Collection<JavaClass> superInterfaces = getAllInterfaces(); |
| |
| for (JavaClass superInterface : superInterfaces) { |
| if (superInterface.equals(inter)) { |
| return true; |
| } |
| } |
| // for (int i = 0; i < super_interfaces.length; i++) { |
| // if (super_interfaces[i].equals(inter)) { |
| // return true; |
| // } |
| // } |
| |
| return false; |
| } |
| |
| /** |
| * @return the superclass for this JavaClass object, or null if this is java.lang.Object |
| */ |
| public JavaClass getSuperClass() { |
| if ("java.lang.Object".equals(getClassName())) { |
| return null; |
| } |
| |
| try { |
| return getRepository().loadClass(getSuperclassName()); |
| } catch (ClassNotFoundException e) { |
| System.err.println(e); |
| return null; |
| } |
| } |
| |
| /** |
| * @return list of super classes of this class in ascending order, i.e., java.lang.Object is always the last element |
| */ |
| public JavaClass[] getSuperClasses() { |
| JavaClass clazz = this; |
| List<JavaClass> vec = new ArrayList<JavaClass>(); |
| for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) { |
| vec.add(clazz); |
| } |
| return vec.toArray(new JavaClass[vec.size()]); |
| } |
| |
| /** |
| * Get interfaces directly implemented by this JavaClass. |
| */ |
| public JavaClass[] getInterfaces() { |
| String[] interfaces = getInterfaceNames(); |
| JavaClass[] classes = new JavaClass[interfaces.length]; |
| |
| try { |
| for (int i = 0; i < interfaces.length; i++) { |
| classes[i] = getRepository().loadClass(interfaces[i]); |
| } |
| } catch (ClassNotFoundException e) { |
| System.err.println(e); |
| return null; |
| } |
| |
| return classes; |
| } |
| |
| /** |
| * Get all interfaces implemented by this JavaClass (transitively). |
| */ |
| public Collection<JavaClass> getAllInterfaces() { |
| Queue<JavaClass> queue = new LinkedList<JavaClass>(); |
| List<JavaClass> interfaceList = new ArrayList<JavaClass>(); |
| |
| queue.add(this); |
| |
| while (!queue.isEmpty()) { |
| JavaClass clazz = queue.remove(); |
| |
| JavaClass souper = clazz.getSuperClass(); |
| JavaClass[] interfaces = clazz.getInterfaces(); |
| |
| if (clazz.isInterface()) { |
| interfaceList.add(clazz); |
| } else { |
| if (souper != null) { |
| queue.add(souper); |
| } |
| } |
| |
| for (int i = 0; i < interfaces.length; i++) { |
| queue.add(interfaces[i]); |
| } |
| } |
| |
| return interfaceList; |
| // return interfaceList.toArray(new JavaClass[interfaceList.size()]); |
| } |
| |
| /** |
| * Hunts for a signature attribute on the member and returns its contents. So where the 'regular' signature may be |
| * Ljava/util/Vector; the signature attribute will tell us e.g. "<E:>Ljava/lang/Object". We can learn the type variable names, |
| * their bounds, and the true superclass and superinterface types (including any parameterizations) Coded for performance - |
| * searches for the attribute only when requested - only searches for it once. |
| */ |
| public final String getGenericSignature() { |
| loadGenericSignatureInfoIfNecessary(); |
| return signatureAttributeString; |
| } |
| |
| public boolean isGeneric() { |
| loadGenericSignatureInfoIfNecessary(); |
| return isGeneric; |
| } |
| |
| private void loadGenericSignatureInfoIfNecessary() { |
| if (!searchedForSignatureAttribute) { |
| signatureAttribute = AttributeUtils.getSignatureAttribute(attributes); |
| signatureAttributeString = signatureAttribute == null ? null : signatureAttribute.getSignature(); |
| isGeneric = signatureAttribute != null && signatureAttributeString.charAt(0) == '<'; |
| searchedForSignatureAttribute = true; |
| } |
| } |
| |
| public final Signature getSignatureAttribute() { |
| loadGenericSignatureInfoIfNecessary(); |
| return signatureAttribute; |
| } |
| |
| } |