All Downloads are FREE. Search and download functionalities are using the official Maven repository.

serp.bytecode.BCClass Maven / Gradle / Ivy

package serp.bytecode;

import java.io.*;
import java.net.*;
import java.util.*;

import serp.bytecode.lowlevel.*;
import serp.bytecode.visitor.*;
import serp.util.*;

/**
 * The BCClass represents a class object in the bytecode framework, in many
 * ways mirroring the {@link Class} class of Java reflection. The represented
 * class might be a primitive, array, existing object type, or some new user-
 * defined type. As with most entities in the bytecode framework, the BCClass
 * contains methods to manipulate the low-level state of the class (constant
 * pool indexes, etc), but these can and should be ignored in
 * favor of the available high-level methods.
 *
 * 

A BCClass instance is loaded from a {@link Project} and remains * attached to that project for its lifetime. If a BCClass is removed from * its project, the result of any further operations on the class are * undefined.

* *

Note that if a BCClass represents a primitive or array type, all of the * available mutator methods and any methods that access the constant pool * will throw {@link UnsupportedOperationException}s.

* * @author Abe White */ public class BCClass extends Annotated implements VisitAcceptor { private Project _project = null; private State _state = null; private ClassLoader _loader = null; /** * Hide constructor. For use by the owning project only. */ BCClass(Project project) { _project = project; } /** * Set the class state. For use by the owning project only. */ void setState(State state) { _state = state; } /** * Invalidate this class. */ void invalidate() { _project = null; _state = State.INVALID; } ////////////////// // I/O operations ////////////////// /** * Initialize from the class definition in the given file. For use by * the owning project only. */ void read(File classFile, ClassLoader loader) throws IOException { InputStream in = new FileInputStream(classFile); try { read(in, loader); } finally { in.close(); } } /** * Initialize from the class definition in the given stream. For use by * the owning project only. */ void read(InputStream instream, ClassLoader loader) throws IOException { DataInput in = new DataInputStream(instream); // header information _state.setMagic(in.readInt()); _state.setMinorVersion(in.readUnsignedShort()); _state.setMajorVersion(in.readUnsignedShort()); // constant pool _state.getPool().read(in); // access flags _state.setAccessFlags(in.readUnsignedShort()); // class, super class, interfaces _state.setIndex(in.readUnsignedShort()); _state.setSuperclassIndex(in.readUnsignedShort()); List interfaces = _state.getInterfacesHolder(); interfaces.clear(); int interfaceCount = in.readUnsignedShort(); for (int i = 0; i < interfaceCount; i++) interfaces.add(Numbers.valueOf(in.readUnsignedShort())); // fields List fields = _state.getFieldsHolder(); fields.clear(); int fieldCount = in.readUnsignedShort(); BCField field; for (int i = 0; i < fieldCount; i++) { field = new BCField(this); fields.add(field); field.read(in); } // methods List methods = _state.getMethodsHolder(); methods.clear(); int methodCount = in.readUnsignedShort(); BCMethod method; for (int i = 0; i < methodCount; i++) { method = new BCMethod(this); methods.add(method); method.read(in); } readAttributes(in); _loader = loader; } /** * Initialize from the bytecode of the definition of the given class. * For use by the owning project only. */ void read(Class type) throws IOException { // find out the length of the package name int dotIndex = type.getName().lastIndexOf('.') + 1; // strip the package off of the class name String className = type.getName().substring(dotIndex); // attempt to get the class file for the class as a stream InputStream in = type.getResourceAsStream(className + ".class"); try { read(in, type.getClassLoader()); } finally { in.close(); } } /** * Initialize from the given parsed bytecode. * For use by the owning project only. */ void read(BCClass orig) { try { ByteArrayInputStream in = new ByteArrayInputStream (orig.toByteArray()); read(in, orig.getClassLoader()); in.close(); } catch (IOException ioe) { throw new RuntimeException(ioe.toString()); } } /** * Write the class bytecode to the .class file in the proper directory of * the CLASSPATH. The file must exist already, so this method only works * on existing classes. */ public void write() throws IOException { String name = getName(); int dotIndex = name.lastIndexOf('.') + 1; name = name.substring(dotIndex); Class type = getType(); // attempt to get the class file for the class as a stream; // we need to use the url decoder in case the target directory // has spaces in it OutputStream out = new FileOutputStream(URLDecoder.decode (type.getResource(name + ".class").getFile())); try { write(out); } finally { out.close(); } } /** * Write the class bytecode to the specified file. */ public void write(File classFile) throws IOException { OutputStream out = new FileOutputStream(classFile); try { write(out); } finally { out.close(); } } /** * Write the class bytecode to the specified stream. */ public void write(OutputStream outstream) throws IOException { DataOutput out = new DataOutputStream(outstream); // header information out.writeInt(_state.getMagic()); out.writeShort(_state.getMinorVersion()); out.writeShort(_state.getMajorVersion()); // constant pool _state.getPool().write(out); // access flags out.writeShort(_state.getAccessFlags()); // class, super class out.writeShort(_state.getIndex()); out.writeShort(_state.getSuperclassIndex()); // interfaces List interfaces = _state.getInterfacesHolder(); out.writeShort(interfaces.size()); for (Iterator itr = interfaces.iterator(); itr.hasNext();) out.writeShort(((Number) itr.next()).intValue()); // fields List fields = _state.getFieldsHolder(); out.writeShort(fields.size()); for (Iterator itr = fields.iterator(); itr.hasNext();) ((BCField) itr.next()).write(out); // methods List methods = _state.getMethodsHolder(); out.writeShort(methods.size()); for (Iterator itr = methods.iterator(); itr.hasNext();) ((BCMethod) itr.next()).write(out); // attributes writeAttributes(out); } /** * Return the bytecode of this class as a byte array, possibly for use * in a custom {@link ClassLoader}. */ public byte[] toByteArray() { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { write(out); out.flush(); return out.toByteArray(); } catch (IOException ioe) { throw new RuntimeException(ioe.toString()); } finally { try { out.close(); } catch (IOException ioe) {} } } ///////////////////// // Access operations ///////////////////// /** * Return the magic number for this class; if this is a valid type, this * should be equal to {@link Constants#VALID_MAGIC} (the default value). */ public int getMagic() { return _state.getMagic(); } /** * Set the magic number for this class; if this is a valid type, this * should be equal to {@link Constants#VALID_MAGIC} (the default value). */ public void setMagic(int magic) { _state.setMagic(magic); } /** * Return the major version of the bytecode spec used for this class. * JVMs are only required to operate with versions that they understand; * leaving the default value of {@link Constants#MAJOR_VERSION} is safe. */ public int getMajorVersion() { return _state.getMajorVersion(); } /** * Set the major version of the bytecode spec used for this class. * JVMs are only required to operate with versions that they understand; * leaving the default value of {@link Constants#MAJOR_VERSION} is safe. */ public void setMajorVersion(int majorVersion) { _state.setMajorVersion(majorVersion); } /** * Get the minor version of the bytecode spec used for this class. * JVMs are only required to operate with versions that they understand; * leaving the default value of {@link Constants#MINOR_VERSION} is safe. */ public int getMinorVersion() { return _state.getMinorVersion(); } /** * Set the minor version of the bytecode spec used for this class. * JVMs are only required to operate with versions that they understand; * leaving the default value of {@link Constants#MINOR_VERSION} is safe. */ public void setMinorVersion(int minorVersion) { _state.setMinorVersion(minorVersion); } /** * Return the access flags for this class as a bit array of * ACCESS_XXX constants from {@link Constants}. This can be used to * transfer access flags between classes without getting/setting each * possible flag. */ public int getAccessFlags() { return _state.getAccessFlags(); } /** * Set the access flags for this class as a bit array of * ACCESS_XXX constants from {@link Constants}. This can be used to * transfer access flags between classes without getting/setting each * possible flag. */ public void setAccessFlags(int access) { _state.setAccessFlags(access); } /** * Manipulate the class access flags. */ public boolean isPublic() { return (getAccessFlags() & Constants.ACCESS_PUBLIC) > 0; } /** * Manipulate the class access flags. */ public void makePublic() { setAccessFlags(getAccessFlags() | Constants.ACCESS_PUBLIC); } /** * Manipulate the class access flags. */ public boolean isPackage() { return !isPublic(); } /** * Manipulate the class access flags. */ public void makePackage() { setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PUBLIC); } /** * Manipulate the class access flags. */ public boolean isFinal() { return (getAccessFlags() & Constants.ACCESS_FINAL) > 0; } /** * Manipulate the class access flags. */ public void setFinal(boolean on) { if (on) setAccessFlags(getAccessFlags() | Constants.ACCESS_FINAL); else setAccessFlags(getAccessFlags() & ~Constants.ACCESS_FINAL); } /** * Manipulate the class access flags. */ public boolean isInterface() { return (getAccessFlags() & Constants.ACCESS_INTERFACE) > 0; } /** * Manipulate the class access flags. */ public void setInterface(boolean on) { if (on) { setAccessFlags(getAccessFlags() | Constants.ACCESS_INTERFACE); setAbstract(true); } else setAccessFlags(getAccessFlags() & ~Constants.ACCESS_INTERFACE); } /** * Manipulate the class access flags. */ public boolean isAbstract() { return (getAccessFlags() & Constants.ACCESS_ABSTRACT) > 0; } /** * Manipulate the class access flags. */ public void setAbstract(boolean on) { if (on) setAccessFlags(getAccessFlags() | Constants.ACCESS_ABSTRACT); else setAccessFlags(getAccessFlags() & ~Constants.ACCESS_ABSTRACT); } /** * Manipulate the class access flags. */ public boolean isSynthetic() { return (getAccessFlags() & Constants.ACCESS_SYNTHETIC) > 0; } /** * Manipulate the class access flags. */ public void setSynthetic(boolean on) { if (on) setAccessFlags(getAccessFlags() | Constants.ACCESS_SYNTHETIC); else setAccessFlags(getAccessFlags() & ~Constants.ACCESS_SYNTHETIC); } /** * Manipulate the class access flags. */ public boolean isAnnotation() { return (getAccessFlags() & Constants.ACCESS_ANNOTATION) > 0; } /** * Manipulate the class access flags. Setting to true also makes this * an interface. */ public void setAnnotation(boolean on) { if (on) { setAccessFlags(getAccessFlags() | Constants.ACCESS_ANNOTATION); setAccessFlags(getAccessFlags() | Constants.ACCESS_INTERFACE); } else setAccessFlags(getAccessFlags() & ~Constants.ACCESS_ANNOTATION); } /** * Manipulate the class access flags. */ public boolean isEnum() { return (getAccessFlags() & Constants.ACCESS_ENUM) > 0; } /** * Manipulate the class access flags. */ public void setEnum(boolean on) { if (on) setAccessFlags(getAccessFlags() | Constants.ACCESS_ENUM); else setAccessFlags(getAccessFlags() & ~Constants.ACCESS_ENUM); } /** * Return true if this class is a primitive type. */ public boolean isPrimitive() { return _state.isPrimitive(); } /** * Return true if this class is an array type. */ public boolean isArray() { return _state.isArray(); } ///////////////////////// // Class name operations ///////////////////////// /** * Return the {@link ConstantPool} index of the * {@link ClassEntry} for this class. Returns 0 if the class does not * have a constant pool (such as a primitive or array). */ public int getIndex() { return _state.getIndex(); } /** * Set the {@link ConstantPool} index of the {@link ClassEntry} for this * class. Unlike most other low-level methods, the index * will be checked against the pool immediately; * classes must have a valid name at all times. */ public void setIndex(int index) { String oldName = getName(); String newName = ((ClassEntry) getPool().getEntry(index)). getNameEntry().getValue(); beforeRename(oldName, newName); _state.setIndex(index); } /** * Return the name of this class, including package name. The name will * be in a form suitable for a {@link Class#forName} call. */ public String getName() { return _state.getName(); } /** * Return the name of the class only, without package. */ public String getClassName() { String name = _project.getNameCache().getExternalForm(getName(), true); return name.substring(name.lastIndexOf('.') + 1); } /** * Return the package name only, without class, or null if none. */ public String getPackageName() { String name = _project.getNameCache().getExternalForm(getName(), true); int index = name.lastIndexOf('.'); if (index == -1) return null; return name.substring(0, index); } /** * Set the name of this class, including package name. */ public void setName(String name) { name = _project.getNameCache().getExternalForm(name, false); String oldName = getName(); // get a reference to the class entry for this class int index = getIndex(); if (index == 0) index = getPool().findClassEntry(name, true); ClassEntry entry = (ClassEntry) getPool().getEntry(index); // make sure the rename is ok with the project beforeRename(oldName, name); // reset the name index of the class entry to the new name int nameIndex = getPool().findUTF8Entry(_project.getNameCache(). getInternalForm(name, false), true); entry.setNameIndex(nameIndex); // we might have just added a new entry; set the index _state.setIndex(index); } /** * Return the {@link Class} object for this class, if it is loadable. */ public Class getType() { return Strings.toClass(getName(), getClassLoader()); } /** * Return the component type name of this class, or null if not an array. * The name will be in a form suitable for a {@link Class#forName} call. */ public String getComponentName() { return _state.getComponentName(); } /** * Return the component type of this class, or null if not an array. */ public Class getComponentType() { String componentName = getComponentName(); if (componentName == null) return null; return Strings.toClass(componentName, getClassLoader()); } /** * Return the component type of this class, or null if not an array. */ public BCClass getComponentBC() { String componentName = getComponentName(); if (componentName == null) return null; return getProject().loadClass(componentName, getClassLoader()); } ///////////////////////// // Superclass operations ///////////////////////// /** * Return the {@link ConstantPool} index of the * {@link ClassEntry} for the superclass of this class. Returns -1 if * the class does not have a constant pool (such as a primitive or array). */ public int getSuperclassIndex() { return _state.getSuperclassIndex(); } /** * Set the {@link ConstantPool} index of the * {@link ClassEntry} for the superclass of this class. */ public void setSuperclassIndex(int index) { _state.setSuperclassIndex(index); } /** * Return the name of the superclass for this class, including package * name. The name will be in a form suitable for a * {@link Class#forName} call, or null for types without superclasses. */ public String getSuperclassName() { return _state.getSuperclassName(); } /** * Return the {@link Class} object for the superclass of this class, if it * is loadable. Returns null for types without superclasses. */ public Class getSuperclassType() { String name = getSuperclassName(); if (name == null) return null; return Strings.toClass(name, getClassLoader()); } /** * Return the bytecode of the superclass of this class, or * null for types without superclasses. */ public BCClass getSuperclassBC() { String name = getSuperclassName(); if (name == null) return null; return getProject().loadClass(name, getClassLoader()); } /** * Set the superclass of this class. */ public void setSuperclass(String name) { if (name == null) setSuperclassIndex(0); else setSuperclassIndex(getPool().findClassEntry(_project.getNameCache(). getInternalForm(name, false), true)); } /** * Set the superclass of this class. */ public void setSuperclass(Class type) { if (type == null) setSuperclass((String) null); else setSuperclass(type.getName()); } /** * Set the superclass of this class. */ public void setSuperclass(BCClass type) { if (type == null) setSuperclass((String) null); else setSuperclass(type.getName()); } //////////////////////// // Interface operations //////////////////////// /** * Return the list of {@link ConstantPool} indexes of the * {@link ClassEntry}s describing all the interfaces this class declares * that it implements/extends. * * @return the implmented interfaces, or an empty array if none */ public int[] getDeclaredInterfaceIndexes() { List interfaces = _state.getInterfacesHolder(); int[] indexes = new int[interfaces.size()]; for (int i = 0; i < interfaces.size(); i++) indexes[i] = ((Number) interfaces.get(i)).intValue(); return indexes; } /** * Set the list of {@link ConstantPool} indexes of the * {@link ClassEntry}s describing all the interfaces this class declares * it implements/extends; set to null or an empty array if none. */ public void setDeclaredInterfaceIndexes(int[] interfaceIndexes) { List stateIndexes = _state.getInterfacesHolder(); stateIndexes.clear(); Integer idx; for (int i = 0; i < interfaceIndexes.length; i++) { idx = Numbers.valueOf(interfaceIndexes[i]); if (!stateIndexes.contains(idx)) stateIndexes.add(idx); } } /** * Return the names of the interfaces declared for this class, including * package names, or an empty array if none. The names will be in a form * suitable for a {@link Class#forName} call. */ public String[] getDeclaredInterfaceNames() { int[] indexes = getDeclaredInterfaceIndexes(); String[] names = new String[indexes.length]; ClassEntry entry; for (int i = 0; i < indexes.length; i++) { entry = (ClassEntry) getPool().getEntry(indexes[i]); names[i] = _project.getNameCache().getExternalForm (entry.getNameEntry().getValue(), false); } return names; } /** * Return the {@link Class} objects for the declared interfaces of this * class, or an empty array if none. */ public Class[] getDeclaredInterfaceTypes() { String[] names = getDeclaredInterfaceNames(); Class[] types = new Class[names.length]; for (int i = 0; i < names.length; i++) types[i] = Strings.toClass(names[i], getClassLoader()); return types; } /** * Return the bytecode for the declared interfaces of this class, or an * empty array if none. */ public BCClass[] getDeclaredInterfaceBCs() { String[] names = getDeclaredInterfaceNames(); BCClass[] types = new BCClass[names.length]; for (int i = 0; i < names.length; i++) types[i] = getProject().loadClass(names[i], getClassLoader()); return types; } /** * Set the interfaces declared implemented/extended by this class; set to * null or an empty array if none. */ public void setDeclaredInterfaces(String[] interfaces) { clearDeclaredInterfaces(); if (interfaces != null) for (int i = 0; i < interfaces.length; i++) declareInterface(interfaces[i]); } /** * Set the interfaces declared implemented/extended by this class; set to * null or an empty array if none. */ public void setDeclaredInterfaces(Class[] interfaces) { String[] names = null; if (interfaces != null) { names = new String[interfaces.length]; for (int i = 0; i < interfaces.length; i++) names[i] = interfaces[i].getName(); } setDeclaredInterfaces(names); } /** * Set the interfaces declared implemented/extended by this class; set to * null or an empty array if none. */ public void setDeclaredInterfaces(BCClass[] interfaces) { String[] names = null; if (interfaces != null) { names = new String[interfaces.length]; for (int i = 0; i < interfaces.length; i++) names[i] = interfaces[i].getName(); } setDeclaredInterfaces(names); } /** * Return the names of all unique interfaces implemented by this class, * including those of all superclasses. The names will be returned in a * form suitable for a {@link Class#forName} call. * This method does not recurse into interfaces-of-interfaces. */ public String[] getInterfaceNames() { Collection allNames = new LinkedList(); String[] names; for (BCClass type = this; type != null; type = type.getSuperclassBC()) { names = type.getDeclaredInterfaceNames(); for (int i = 0; i < names.length; i++) allNames.add(names[i]); } return (String[]) allNames.toArray(new String[allNames.size()]); } /** * Return the {@link Class} objects of all unique interfaces implemented * by this class, including those of all superclasses. * This method does not recurse into interfaces-of-interfaces. */ public Class[] getInterfaceTypes() { Collection allTypes = new LinkedList(); Class[] types; for (BCClass type = this; type != null; type = type.getSuperclassBC()) { types = type.getDeclaredInterfaceTypes(); for (int i = 0; i < types.length; i++) allTypes.add(types[i]); } return (Class[]) allTypes.toArray(new Class[allTypes.size()]); } /** * Return the bytecode of all unique interfaces implemented by this class, * including those of all superclasses. * This method does not recurse into interfaces-of-interfaces. */ public BCClass[] getInterfaceBCs() { Collection allTypes = new LinkedList(); BCClass[] types; for (BCClass type = this; type != null; type = type.getSuperclassBC()) { types = type.getDeclaredInterfaceBCs(); for (int i = 0; i < types.length; i++) allTypes.add(types[i]); } return (BCClass[]) allTypes.toArray(new BCClass[allTypes.size()]); } /** * Clear this class of all interface declarations. */ public void clearDeclaredInterfaces() { _state.getInterfacesHolder().clear(); } /** * Remove an interface declared by this class. * * @return true if the class had the interface, false otherwise */ public boolean removeDeclaredInterface(String name) { String[] names = getDeclaredInterfaceNames(); Iterator itr = _state.getInterfacesHolder().iterator(); for (int i = 0; i < names.length; i++) { itr.next(); if (names[i].equals(name)) { itr.remove(); return true; } } return false; } /** * Remove an interface declared by this class. * * @return true if the class had the interface, false otherwise */ public boolean removeDeclaredInterface(Class type) { if (type == null) return false; return removeDeclaredInterface(type.getName()); } /** * Remove an interface declared by this class. * * @return true if the class had the interface, false otherwise */ public boolean removeDeclaredInterface(BCClass type) { if (type == null) return false; return removeDeclaredInterface(type.getName()); } /** * Rearrange declared interface order. */ public void moveDeclaredInterface(int fromIdx, int toIdx) { if (fromIdx == toIdx) return; List interfaces = _state.getInterfacesHolder(); Object o = interfaces.remove(fromIdx); interfaces.add(toIdx, o); } /** * Add an interface to those declared by this class. */ public void declareInterface(String name) { Integer index = Numbers.valueOf(getPool().findClassEntry(_project. getNameCache().getInternalForm(name, false), true)); List interfaces = _state.getInterfacesHolder(); if (!interfaces.contains(index)) interfaces.add(index); } /** * Add an interface to those declared by this class. */ public void declareInterface(Class type) { declareInterface(type.getName()); } /** * Add an interface to those declared by this class. */ public void declareInterface(BCClass type) { declareInterface(type.getName()); } /** * Return true if this class or any of its superclasses implement/extend * the given interface/class. * This method does not recurse into interfaces-of-interfaces. */ public boolean isInstanceOf(String name) { name = _project.getNameCache().getExternalForm(name, false); String[] interfaces = getInterfaceNames(); for (int i = 0; i < interfaces.length; i++) if (interfaces[i].equals(name)) return true; for (BCClass type = this; type != null; type = type.getSuperclassBC()) if (type.getName().equals(name)) return true; return false; } /** * Return true if this class or any of its superclasses implement/extend * the given interface/class. * This method does not recurse into interfaces-of-interfaces. */ public boolean isInstanceOf(Class type) { if (type == null) return false; return isInstanceOf(type.getName()); } /** * Return true if this class or any of its superclasses implement/extend * the given interface/class. * This method does not recurse into interfaces-of-interfaces. */ public boolean isInstanceOf(BCClass type) { if (type == null) return false; return isInstanceOf(type.getName()); } ////////////////////// // Field operations ////////////////////// /** * Return all the declared fields of this class, or an empty array if none. */ public BCField[] getDeclaredFields() { List fields = _state.getFieldsHolder(); return (BCField[]) fields.toArray(new BCField[fields.size()]); } /** * Return the declared field with the given name, or null if none. */ public BCField getDeclaredField(String name) { BCField[] fields = getDeclaredFields(); for (int i = 0; i < fields.length; i++) if (fields[i].getName().equals(name)) return fields[i]; return null; } /** * Return all the fields of this class, including those of all * superclasses, or an empty array if none. */ public BCField[] getFields() { Collection allFields = new LinkedList(); BCField[] fields; for (BCClass type = this; type != null; type = type.getSuperclassBC()) { fields = type.getDeclaredFields(); for (int i = 0; i < fields.length; i++) allFields.add(fields[i]); } return (BCField[]) allFields.toArray(new BCField[allFields.size()]); } /** * Return all fields with the given name, including those of all * superclasses, or an empty array if none. */ public BCField[] getFields(String name) { List matches = new LinkedList(); BCField[] fields = getFields(); for (int i = 0; i < fields.length; i++) if (fields[i].getName().equals(name)) matches.add(fields[i]); return (BCField[]) matches.toArray(new BCField[matches.size()]); } /** * Set the fields for this class; this method is useful for importing all * fields from another class. Set to null or empty array if none. */ public void setDeclaredFields(BCField[] fields) { clearDeclaredFields(); if (fields != null) for (int i = 0; i < fields.length; i++) declareField(fields[i]); } /** * Import the information from given field as a new field in this class. * * @return the added field */ public BCField declareField(BCField field) { BCField newField = declareField(field.getName(), field.getTypeName()); newField.setAccessFlags(field.getAccessFlags()); newField.setAttributes(field.getAttributes()); return newField; } /** * Add a field to this class. * * @return the added field */ public BCField declareField(String name, String type) { BCField field = new BCField(this); _state.getFieldsHolder().add(field); field.initialize(name, _project.getNameCache().getInternalForm(type, true)); return field; } /** * Add a field to this class. * * @return the added field */ public BCField declareField(String name, Class type) { String typeName = (type == null) ? null : type.getName(); return declareField(name, typeName); } /** * Add a field to this class. * * @return the added field */ public BCField declareField(String name, BCClass type) { String typeName = (type == null) ? null : type.getName(); return declareField(name, typeName); } /** * Clear all fields from this class. */ public void clearDeclaredFields() { List fields = _state.getFieldsHolder(); BCField field; for (Iterator itr = fields.iterator(); itr.hasNext();) { field = (BCField) itr.next(); itr.remove(); field.invalidate(); } } /** * Remove a field from this class. After this method, the removed field * will be invalid, and the result of any operations on it is undefined. * * @return true if this class contained the field, false otherwise */ public boolean removeDeclaredField(String name) { List fields = _state.getFieldsHolder(); BCField field; for (Iterator itr = fields.iterator(); itr.hasNext();) { field = (BCField) itr.next(); if (field.getName().equals(name)) { itr.remove(); field.invalidate(); return true; } } return false; } /** * Remove a field from this class. After this method, the removed field * will be invalid, and the result of any operations on it is undefined. * * @return true if this class contained the field, false otherwise */ public boolean removeDeclaredField(BCField field) { if (field == null) return false; return removeDeclaredField(field.getName()); } /** * Rearrange declared field order. */ public void moveDeclaredField(int fromIdx, int toIdx) { if (fromIdx == toIdx) return; List fields = _state.getFieldsHolder(); Object o = fields.remove(fromIdx); fields.add(toIdx, o); } ////////////////////// // Method operations ////////////////////// /** * Return all methods declared by this class. Constructors and static * initializers are included. */ public BCMethod[] getDeclaredMethods() { List methods = _state.getMethodsHolder(); return (BCMethod[]) methods.toArray(new BCMethod[methods.size()]); } /** * Return the declared method with the given name, or null if none. * If multiple methods are declared with the given name, which is returned * is undefined. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. */ public BCMethod getDeclaredMethod(String name) { BCMethod[] methods = getDeclaredMethods(); for (int i = 0; i < methods.length; i++) if (methods[i].getName().equals(name)) return methods[i]; return null; } /** * Return all the declared methods with the given name, or an empty array * if none. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. */ public BCMethod[] getDeclaredMethods(String name) { Collection matches = new LinkedList(); BCMethod[] methods = getDeclaredMethods(); for (int i = 0; i < methods.length; i++) if (methods[i].getName().equals(name)) matches.add(methods[i]); return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]); } /** * Return the declared method with the given name and parameter types, * or null if none. If multiple methods are declared with the given name * and parameters, which is returned is undefined. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. */ public BCMethod getDeclaredMethod(String name, String[] paramTypes) { if (paramTypes == null) paramTypes = new String[0]; BCMethod[] methods = getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { if (methods[i].getName().equals(name) && paramsMatch(methods[i], paramTypes)) return methods[i]; } return null; } /** * Return true iff the given method's parameters match params. */ private boolean paramsMatch(BCMethod meth, String[] params) { String[] mparams = meth.getParamNames(); if (mparams.length != params.length) return false; for (int i = 0; i < params.length; i++) { if (!mparams[i].equals(_project.getNameCache(). getExternalForm(params[i], false))) return false; } return true; } /** * Return the declared method with the given name and parameter types, * or null if none. If multiple methods are declared with the given name * and parameters, which is returned is undefined. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. */ public BCMethod getDeclaredMethod(String name, Class[] paramTypes) { if (paramTypes == null) return getDeclaredMethod(name, (String[]) null); String[] paramNames = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) paramNames[i] = paramTypes[i].getName(); return getDeclaredMethod(name, paramNames); } /** * Return the declared method with the given name and parameter types, * or null if none. If multiple methods are declared with the given name * and parameters, which is returned is undefined. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. */ public BCMethod getDeclaredMethod(String name, BCClass[] paramTypes) { if (paramTypes == null) return getDeclaredMethod(name, (String[]) null); String[] paramNames = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) paramNames[i] = paramTypes[i].getName(); return getDeclaredMethod(name, paramNames); } /** * Return all declared methods with the given name and parameter types. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. */ public BCMethod[] getDeclaredMethods(String name, String[] paramTypes) { if (paramTypes == null) paramTypes = new String[0]; BCMethod[] methods = getDeclaredMethods(); List matches = null; for (int i = 0; i < methods.length; i++) { if (methods[i].getName().equals(name) && paramsMatch(methods[i], paramTypes)) { if (matches == null) matches = new ArrayList(3); matches.add(methods[i]); } } if (matches == null) return new BCMethod[0]; return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]); } /** * Return all declared methods with the given name and parameter types. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. */ public BCMethod[] getDeclaredMethods(String name, Class[] paramTypes) { if (paramTypes == null) return getDeclaredMethods(name, (String[]) null); String[] paramNames = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) paramNames[i] = paramTypes[i].getName(); return getDeclaredMethods(name, paramNames); } /** * Return all declared methods with the given name and parameter types. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. */ public BCMethod[] getDeclaredMethods(String name, BCClass[] paramTypes) { if (paramTypes == null) return getDeclaredMethods(name, (String[]) null); String[] paramNames = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) paramNames[i] = paramTypes[i].getName(); return getDeclaredMethods(name, paramNames); } /** * Return the declared method with the given name and signature, * or null if none. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. */ public BCMethod getDeclaredMethod(String name, String returnType, String[] paramTypes) { if (paramTypes == null) paramTypes = new String[0]; BCMethod[] methods = getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { if (methods[i].getName().equals(name) && methods[i].getReturnName().equals(_project.getNameCache(). getExternalForm(returnType, false)) && paramsMatch(methods[i], paramTypes)) return methods[i]; } return null; } /** * Return the declared method with the given name and signature, * or null if none. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. */ public BCMethod getDeclaredMethod(String name, Class returnType, Class[] paramTypes) { if (paramTypes == null) return getDeclaredMethod(name, returnType.getName(), (String[]) null); String[] paramNames = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) paramNames[i] = paramTypes[i].getName(); return getDeclaredMethod(name, returnType.getName(), paramNames); } /** * Return the declared method with the given name and signature, * or null if none. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. */ public BCMethod getDeclaredMethod(String name, BCClass returnType, BCClass[] paramTypes) { if (paramTypes == null) return getDeclaredMethod(name, returnType.getName(), (String[]) null); String[] paramNames = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) paramNames[i] = paramTypes[i].getName(); return getDeclaredMethod(name, returnType.getName(), paramNames); } /** * Return the methods of this class, including those of all superclasses, * or an empty array if none. * The base version of methods that are overridden will be included, as * will all constructors and static initializers. * The methods will be ordered from those in the most-specific type up to * those in {@link Object}. */ public BCMethod[] getMethods() { Collection allMethods = new LinkedList(); BCMethod[] methods; for (BCClass type = this; type != null; type = type.getSuperclassBC()) { methods = type.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) allMethods.add(methods[i]); } return (BCMethod[]) allMethods.toArray(new BCMethod[allMethods.size()]); } /** * Return the methods with the given name, including those of all * superclasses, or an empty array if none. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. */ public BCMethod[] getMethods(String name) { Collection matches = new LinkedList(); BCMethod[] methods = getMethods(); for (int i = 0; i < methods.length; i++) if (methods[i].getName().equals(name)) matches.add(methods[i]); return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]); } /** * Return the methods with the given name and parameter types, including * those of all superclasses, or an empty array if none. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. */ public BCMethod[] getMethods(String name, String[] paramTypes) { if (paramTypes == null) paramTypes = new String[0]; String[] curParams; boolean match; BCMethod[] methods = getMethods(); Collection matches = new LinkedList(); for (int i = 0; i < methods.length; i++) { if (!methods[i].getName().equals(name)) continue; curParams = methods[i].getParamNames(); if (curParams.length != paramTypes.length) continue; match = true; for (int j = 0; j < paramTypes.length; j++) { if (!curParams[j].equals(_project.getNameCache(). getExternalForm(paramTypes[j], false))) { match = false; break; } } if (match) matches.add(methods[i]); } return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]); } /** * Return the methods with the given name and parameter types, including * those of all superclasses, or an empty array if none. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. */ public BCMethod[] getMethods(String name, Class[] paramTypes) { if (paramTypes == null) return getMethods(name, (String[]) null); String[] paramNames = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) paramNames[i] = paramTypes[i].getName(); return getMethods(name, paramNames); } /** * Return the methods with the given name and parameter types, including * those of all superclasses, or an empty array if none. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. */ public BCMethod[] getMethods(String name, BCClass[] paramTypes) { if (paramTypes == null) return getMethods(name, (String[]) null); String[] paramNames = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) paramNames[i] = paramTypes[i].getName(); return getMethods(name, paramNames); } /** * Set the methods for this class; this method is useful for importing all * methods from another class. Set to null or empty array if none. */ public void setDeclaredMethods(BCMethod[] methods) { clearDeclaredMethods(); if (methods != null) for (int i = 0; i < methods.length; i++) declareMethod(methods[i]); } /** * Import the information in the given method as a new method of this class. * * @return the added method */ public BCMethod declareMethod(BCMethod method) { BCMethod newMethod = declareMethod(method.getName(), method.getReturnName(), method.getParamNames()); newMethod.setAccessFlags(method.getAccessFlags()); newMethod.setAttributes(method.getAttributes()); return newMethod; } /** * Add a method to this class. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. * * @return the added method */ public BCMethod declareMethod(String name, String returnType, String[] paramTypes) { BCMethod method = new BCMethod(this); _state.getMethodsHolder().add(method); method.initialize(name, _project.getNameCache(). getDescriptor(returnType, paramTypes)); return method; } /** * Add a method to this class. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. * * @return the added method */ public BCMethod declareMethod(String name, Class returnType, Class[] paramTypes) { String[] paramNames = null; if (paramTypes != null) { paramNames = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) paramNames[i] = paramTypes[i].getName(); } String returnName = (returnType == null) ? null : returnType.getName(); return declareMethod(name, returnName, paramNames); } /** * Add a method to this class. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. * * @return the added method */ public BCMethod declareMethod(String name, BCClass returnType, BCClass[] paramTypes) { String[] paramNames = null; if (paramTypes != null) { paramNames = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) paramNames[i] = paramTypes[i].getName(); } String returnName = (returnType == null) ? null : returnType.getName(); return declareMethod(name, returnName, paramNames); } /** * Clear all declared methods from this class. */ public void clearDeclaredMethods() { List methods = _state.getMethodsHolder(); BCMethod method; for (Iterator itr = methods.iterator(); itr.hasNext();) { method = (BCMethod) itr.next(); itr.remove(); method.invalidate(); } } /** * Remove a method from this class. After this method, the removed method * will be invalid, and the result of any operations on it is undefined. * If multiple methods match the given name, which is removed is undefined. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. * * @return true if this class contained the method, false otherwise */ public boolean removeDeclaredMethod(String name) { List methods = _state.getMethodsHolder(); BCMethod method; for (Iterator itr = methods.iterator(); itr.hasNext();) { method = (BCMethod) itr.next(); if (method.getName().equals(name)) { itr.remove(); method.invalidate(); return true; } } return false; } /** * Removes a method from this class. After this method, the removed method * will be invalid, and the result of any operations on it is undefined. * * @return true if this class contained the method, false otherwise */ public boolean removeDeclaredMethod(BCMethod method) { if (method == null) return false; return removeDeclaredMethod(method.getName(), method.getParamNames()); } /** * Removes a method from this class. After this method, the removed method * will be invalid, and the result of any operations on it is undefined. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. * * @return true if this class contained the method, false otherwise */ public boolean removeDeclaredMethod(String name, String[] paramTypes) { if (paramTypes == null) paramTypes = new String[0]; String[] curParams; boolean match; List methods = _state.getMethodsHolder(); BCMethod method; for (Iterator itr = methods.iterator(); itr.hasNext();) { method = (BCMethod) itr.next(); if (!method.getName().equals(name)) continue; curParams = method.getParamNames(); if (curParams.length != paramTypes.length) continue; match = true; for (int j = 0; j < paramTypes.length; j++) { if (!curParams[j].equals(_project.getNameCache(). getExternalForm(paramTypes[j], false))) { match = false; break; } } if (match) { itr.remove(); method.invalidate(); return true; } } return false; } /** * Removes a method from this class. After this method, the removed method * will be invalid, and the result of any operations on it is undefined. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. * * @return true if this class contained the method, false otherwise */ public boolean removeDeclaredMethod(String name, Class[] paramTypes) { if (paramTypes == null) return removeDeclaredMethod(name, (String[]) null); String[] paramNames = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) paramNames[i] = paramTypes[i].getName(); return removeDeclaredMethod(name, paramNames); } /** * Removes a method from this class. After this method, the removed method * will be invalid, and the result of any operations on it is undefined. * Note that in bytecode, constructors are named <init> * and static initializers are named <clinit>. * * @return true if this class contained the method, false otherwise */ public boolean removeDeclaredMethod(String name, BCClass[] paramTypes) { if (paramTypes == null) return removeDeclaredMethod(name, (String[]) null); String[] paramNames = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) paramNames[i] = paramTypes[i].getName(); return removeDeclaredMethod(name, paramNames); } /** * Rearrange method order. */ public void moveDeclaredMethod(int fromIdx, int toIdx) { if (fromIdx == toIdx) return; List methods = _state.getMethodsHolder(); Object o = methods.remove(fromIdx); methods.add(toIdx, o); } /////////////////////// // Convenience methods /////////////////////// /** * Convenience method to add a default constructor to this class. * If a default constructor already exists, this method will return it * without modification. * This method can only be called if the superclass has been set. * * @return the default constructor */ public BCMethod addDefaultConstructor() { BCMethod method = getDeclaredMethod("", (String[]) null); if (method != null) return method; method = declareMethod("", void.class, null); Code code = method.getCode(true); code.setMaxStack(1); code.setMaxLocals(1); code.xload().setThis(); code.invokespecial() .setMethod(getSuperclassName(), "", "void", null); code.vreturn(); return method; } /** * Return source file information for the class. * Acts internally through the {@link Attributes} interface. * * @param add if true, a new source file attribute will be added * if not already present * @return the source file information, or null if none and the * add param is set to false */ public SourceFile getSourceFile(boolean add) { SourceFile source = (SourceFile) getAttribute(Constants.ATTR_SOURCE); if (!add || (source != null)) return source; return (SourceFile) addAttribute(Constants.ATTR_SOURCE); } /** * Remove the source file attribute for the class. * Acts internally through the {@link Attributes} interface. * * @return true if there was a file to remove */ public boolean removeSourceFile() { return removeAttribute(Constants.ATTR_SOURCE); } /** * Return inner classes information for the class. * Acts internally through the {@link Attributes} interface. * * @param add if true, a new inner classes attribute will be added * if not already present * @return the inner classes information, or null if none and the * add param is set to false */ public InnerClasses getInnerClasses(boolean add) { InnerClasses inner = (InnerClasses) getAttribute (Constants.ATTR_INNERCLASS); if (!add || (inner != null)) return inner; return (InnerClasses) addAttribute(Constants.ATTR_INNERCLASS); } /** * Remove the inner classes attribute for the class. * Acts internally through the {@link Attributes} interface. * * @return true if there was an attribute to remove */ public boolean removeInnerClasses() { return removeAttribute(Constants.ATTR_INNERCLASS); } /** * Convenience method to return deprecation information for the class. * Acts internally through the {@link Attributes} interface. */ public boolean isDeprecated() { return getAttribute(Constants.ATTR_DEPRECATED) != null; } /** * Convenience method to set whether this class should be considered * deprecated. Acts internally through the {@link Attributes} interface. */ public void setDeprecated(boolean on) { if (!on) removeAttribute(Constants.ATTR_DEPRECATED); else if (!isDeprecated()) addAttribute(Constants.ATTR_DEPRECATED); } /////////////////////////////////// // Implementation of VisitAcceptor /////////////////////////////////// public void acceptVisit(BCVisitor visit) { visit.enterBCClass(this); ConstantPool pool = null; try { pool = _state.getPool(); } catch (UnsupportedOperationException uoe) { } if (pool != null) pool.acceptVisit(visit); BCField[] fields = getDeclaredFields(); for (int i = 0; i < fields.length; i++) { visit.enterBCMember(fields[i]); fields[i].acceptVisit(visit); visit.exitBCMember(fields[i]); } BCMethod[] methods = getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { visit.enterBCMember(methods[i]); methods[i].acceptVisit(visit); visit.exitBCMember(methods[i]); } visitAttributes(visit); visit.exitBCClass(this); } //////////////////////////////// // Implementation of Attributes //////////////////////////////// public Project getProject() { return _project; } public ConstantPool getPool() { return _state.getPool(); } public ClassLoader getClassLoader() { if (_loader != null) return _loader; return Thread.currentThread().getContextClassLoader(); } public boolean isValid() { return _project != null; } Collection getAttributesHolder() { return _state.getAttributesHolder(); } /////////////////////////////// // Implementation of Annotated /////////////////////////////// BCClass getBCClass() { return this; } /** * Attempts to change the class name with the owning project. The project * can reject the change if a class with the given new name already * exists; therefore this method should be called before the change is * recorded in the class. */ private void beforeRename(String oldName, String newName) { if ((_project != null) && (oldName != null)) _project.renameClass(oldName, newName, this); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy