<zip zipfile="${dist-version}.zip">
<zipfileset dir="${basedir}" prefix="${dist-version}">
<include name="**"/>
+ <exclude name=".*"/>
<exclude name="build/**"/>
<exclude name="local/**"/>
<exclude name="${dist-version}.zip"/>
* to find a class file and then it creates a <code>CtClass</code> object
* representing that class file. The created object is returned to the
* caller.
- * <p/>
+ *
* <p><b>Memory consumption memo:</b>
- * <p/>
+ *
* <p><code>ClassPool</code> objects hold all the <code>CtClass</code>es
* that have been created so that the consistency among modified classes
* can be guaranteed. Thus if a large number of <code>CtClass</code>es
* Note that <code>getDefault()</code> is a singleton factory.
* Otherwise, <code>detach()</code> in <code>CtClass</code> should be used
* to avoid huge memory consumption.
- * <p/>
+ *
* <p><b><code>ClassPool</code> hierarchy:</b>
- * <p/>
+ *
* <p><code>ClassPool</code>s can make a parent-child hierarchy as
* <code>java.lang.ClassLoader</code>s. If a <code>ClassPool</code> has
* a parent pool, <code>get()</code> first asks the parent pool to find
* @see javassist.CtClass
* @see javassist.ClassPath
*/
-public class ClassPool
-{
-
- /**
- * Determines the search order.
- * <p/>
- * <p>If this field is true, <code>get()</code> first searches the
- * class path associated to this <code>ClassPool</code> and then
- * the class path associated with the parent <code>ClassPool</code>.
- * Otherwise, the class path associated with the parent is searched
- * first.
- * <p/>
- * <p>The default value is false.
- */
- public boolean childFirstLookup = false;
-
- protected ClassPoolTail source;
- protected ClassPool parent;
- protected Hashtable classes; // should be synchronous
-
- /**
- * Table of registered cflow variables.
- */
- private Hashtable cflow = null; // should be synchronous.
-
- private static final int INIT_HASH_SIZE = 191;
-
- /**
- * Creates a root class pool. No parent class pool is specified.
- */
- public ClassPool()
- {
- this(null);
- }
-
- /**
- * Creates a class pool.
- *
- * @param parent the parent of this class pool. If this is a root
- * class pool, this parameter must be <code>null</code>.
- * @see javassist.ClassPool#getDefault()
- */
- public ClassPool(ClassPool parent)
- {
- this.classes = new Hashtable(INIT_HASH_SIZE);
- this.source = new ClassPoolTail();
- this.parent = parent;
- if (parent == null)
- {
- CtClass[] pt = CtClass.primitiveTypes;
- for (int i = 0; i < pt.length; ++i)
- classes.put(pt[i].getName(), pt[i]);
- }
-
- this.cflow = null;
- }
-
- /**
- * Returns the default class pool.
- * The returned object is always identical since this method is
- * a singleton factory.
- * <p/>
- * <p>The default class pool searches the system search path,
- * which usually includes the platform library, extension
- * libraries, and the search path specified by the
- * <code>-classpath</code> option or the <code>CLASSPATH</code>
- * environment variable.
- * <p/>
- * <p>When this method is called for the first time, the default
- * class pool is created with the following code snippet:
- * <p/>
- * <ul><code>ClassPool cp = new ClassPool();
- * cp.appendSystemPath();
- * </code></ul>
- * <p/>
- * <p>If the default class pool cannot find any class files,
- * try <code>ClassClassPath</code> and <code>LoaderClassPath</code>.
- *
- * @see ClassClassPath
- * @see LoaderClassPath
- */
- public static synchronized ClassPool getDefault()
- {
- if (defaultPool == null)
- {
- defaultPool = new ClassPool(null);
- defaultPool.appendSystemPath();
- }
-
- return defaultPool;
- }
-
- private static ClassPool defaultPool = null;
-
- /**
- * Provide a hook so that subclasses can do their own
- * caching of classes.
- *
- * @see #cacheCtClass(String,CtClass)
- * @see #removeCached(String)
- */
- protected CtClass getCached(String classname)
- {
- return (CtClass) classes.get(classname);
- }
-
- /**
- * Provides a hook so that subclasses can do their own
- * caching of classes.
- *
- * @see #getCached(String)
- * @see #removeCached(String,CtClass)
- */
- protected void cacheCtClass(String classname, CtClass c)
- {
- classes.put(classname, c);
- }
-
- /**
- * Provide a hook so that subclasses can do their own
- * caching of classes.
- *
- * @see #getCached(String)
- * @see #cacheCtClass(String,CtClass)
- */
- protected CtClass removeCached(String classname)
- {
- return (CtClass) classes.remove(classname);
- }
-
- /**
- * Returns the class search path.
- */
- public String toString()
- {
- return source.toString();
- }
-
- /**
- * Records a name that never exists.
- * For example, a package name can be recorded by this method.
- * This would improve execution performance
- * since <code>get()</code> does not search the class path at all
- * if the given name is an invalid name recorded by this method.
- * Note that searching the class path takes relatively long time.
- *
- * @param name a class name (separeted by dot).
- */
- public void recordInvalidClassName(String name)
- {
- source.recordInvalidClassName(name);
- }
-
- /**
- * Records the <code>$cflow</code> variable for the field specified
- * by <code>cname</code> and <code>fname</code>.
- *
- * @param name variable name
- * @param cname class name
- * @param fname field name
- */
- void recordCflow(String name, String cname, String fname)
- {
- if (cflow == null)
- cflow = new Hashtable();
-
- cflow.put(name, new Object[]{cname, fname});
- }
-
- /**
- * Undocumented method. Do not use; internal-use only.
- *
- * @param name the name of <code>$cflow</code> variable
- */
- public Object[] lookupCflow(String name)
- {
- if (cflow == null)
- cflow = new Hashtable();
-
- return (Object[]) cflow.get(name);
- }
-
- /**
- * Reads a class file and constructs a <code>CtClass</code>
- * object with a new name.
- * This method is useful if you want to generate a new class as a copy
- * of another class (except the class name). For example,
- * <p/>
- * <ul><pre>
- * getAndRename("Point", "Pair")
- * </pre></ul>
- * <p/>
- * returns a <code>CtClass</code> object representing <code>Pair</code>
- * class. The definition of <code>Pair</code> is the same as that of
- * <code>Point</code> class except the class name since <code>Pair</code>
- * is defined by reading <code>Point.class</code>.
- *
- * @param orgName the original (fully-qualified) class name
- * @param newName the new class name
- */
- public CtClass getAndRename(String orgName, String newName)
- throws NotFoundException
- {
- CtClass clazz = get0(orgName, false);
- if (clazz instanceof CtClassType)
- ((CtClassType) clazz).setClassPool(this);
-
- clazz.setName(newName); // indirectly calls
- // classNameChanged() in this class
- return clazz;
- }
-
- /*
- * This method is invoked by CtClassType.setName(). It removes a
- * CtClass object from the hash table and inserts it with the new
- * name. Don't delegate to the parent.
- */
- synchronized void classNameChanged(String oldname, CtClass clazz)
- {
- CtClass c = (CtClass) getCached(oldname);
- if (c == clazz) // must check this equation.
- removeCached(oldname); // see getAndRename().
-
- String newName = clazz.getName();
- checkNotFrozen(newName);
- cacheCtClass(newName, clazz);
- }
-
- /**
- * Reads a class file from the source and returns a reference
- * to the <code>CtClass</code>
- * object representing that class file. If that class file has been
- * already read, this method returns a reference to the
- * <code>CtClass</code> created when that class file was read at the
- * first time.
- * <p/>
- * <p>If <code>classname</code> ends with "[]", then this method
- * returns a <code>CtClass</code> object for that array type.
- * <p/>
- * <p>To obtain an inner class, use "$" instead of "." for separating
- * the enclosing class name and the inner class name.
- *
- * @param classname a fully-qualified class name.
- */
- public CtClass get(String classname) throws NotFoundException
- {
- CtClass clazz;
- if (classname == null)
- clazz = null;
- else
- clazz = get0(classname, true);
-
- if (clazz == null)
- throw new NotFoundException(classname);
- else
- {
- clazz.incGetCounter();
- return clazz;
- }
- }
-
- /**
- * @param useCache false if the cached CtClass must be ignored.
- * @param searchParent false if the parent class pool is not searched.
- * @return null if the class could not be found.
- */
- protected synchronized CtClass get0(String classname, boolean useCache)
- throws NotFoundException
- {
- CtClass clazz = null;
- if (useCache)
- {
- clazz = getCached(classname);
- if (clazz != null)
+public class ClassPool {
+
+ /**
+ * Determines the search order.
+ *
+ * <p>If this field is true, <code>get()</code> first searches the
+ * class path associated to this <code>ClassPool</code> and then
+ * the class path associated with the parent <code>ClassPool</code>.
+ * Otherwise, the class path associated with the parent is searched
+ * first.
+ *
+ * <p>The default value is false.
+ */
+ public boolean childFirstLookup = false;
+
+ protected ClassPoolTail source;
+ protected ClassPool parent;
+ protected Hashtable classes; // should be synchronous
+
+ /**
+ * Table of registered cflow variables.
+ */
+ private Hashtable cflow = null; // should be synchronous.
+
+ private static final int INIT_HASH_SIZE = 191;
+
+ /**
+ * Creates a root class pool. No parent class pool is specified.
+ */
+ public ClassPool() {
+ this(null);
+ }
+
+ /**
+ * Creates a class pool.
+ *
+ * @param parent the parent of this class pool. If this is a root
+ * class pool, this parameter must be <code>null</code>.
+ * @see javassist.ClassPool#getDefault()
+ */
+ public ClassPool(ClassPool parent) {
+ this.classes = new Hashtable(INIT_HASH_SIZE);
+ this.source = new ClassPoolTail();
+ this.parent = parent;
+ if (parent == null) {
+ CtClass[] pt = CtClass.primitiveTypes;
+ for (int i = 0; i < pt.length; ++i)
+ classes.put(pt[i].getName(), pt[i]);
+ }
+
+ this.cflow = null;
+ }
+
+ /**
+ * Returns the default class pool.
+ * The returned object is always identical since this method is
+ * a singleton factory.
+ *
+ * <p>The default class pool searches the system search path,
+ * which usually includes the platform library, extension
+ * libraries, and the search path specified by the
+ * <code>-classpath</code> option or the <code>CLASSPATH</code>
+ * environment variable.
+ *
+ * <p>When this method is called for the first time, the default
+ * class pool is created with the following code snippet:
+ *
+ * <ul><code>ClassPool cp = new ClassPool();
+ * cp.appendSystemPath();
+ * </code></ul>
+ *
+ * <p>If the default class pool cannot find any class files,
+ * try <code>ClassClassPath</code> and <code>LoaderClassPath</code>.
+ *
+ * @see ClassClassPath
+ * @see LoaderClassPath
+ */
+ public static synchronized ClassPool getDefault() {
+ if (defaultPool == null) {
+ defaultPool = new ClassPool(null);
+ defaultPool.appendSystemPath();
+ }
+
+ return defaultPool;
+ }
+
+ private static ClassPool defaultPool = null;
+
+ /**
+ * Provide a hook so that subclasses can do their own
+ * caching of classes.
+ *
+ * @see #cacheCtClass(String,CtClass)
+ * @see #removeCached(String)
+ */
+ protected CtClass getCached(String classname) {
+ return (CtClass)classes.get(classname);
+ }
+
+ /**
+ * Provides a hook so that subclasses can do their own
+ * caching of classes.
+ *
+ * @see #getCached(String)
+ * @see #removeCached(String,CtClass)
+ */
+ protected void cacheCtClass(String classname, CtClass c) {
+ classes.put(classname, c);
+ }
+
+ /**
+ * Provide a hook so that subclasses can do their own
+ * caching of classes.
+ *
+ * @see #getCached(String)
+ * @see #cacheCtClass(String,CtClass)
+ */
+ protected CtClass removeCached(String classname) {
+ return (CtClass)classes.remove(classname);
+ }
+
+ /**
+ * Returns the class search path.
+ */
+ public String toString() {
+ return source.toString();
+ }
+
+ /**
+ * Records a name that never exists.
+ * For example, a package name can be recorded by this method.
+ * This would improve execution performance
+ * since <code>get()</code> does not search the class path at all
+ * if the given name is an invalid name recorded by this method.
+ * Note that searching the class path takes relatively long time.
+ *
+ * @param name a class name (separeted by dot).
+ */
+ public void recordInvalidClassName(String name) {
+ source.recordInvalidClassName(name);
+ }
+
+ /**
+ * Records the <code>$cflow</code> variable for the field specified
+ * by <code>cname</code> and <code>fname</code>.
+ *
+ * @param name variable name
+ * @param cname class name
+ * @param fname field name
+ */
+ void recordCflow(String name, String cname, String fname) {
+ if (cflow == null)
+ cflow = new Hashtable();
+
+ cflow.put(name, new Object[] { cname, fname });
+ }
+
+ /**
+ * Undocumented method. Do not use; internal-use only.
+ *
+ * @param name the name of <code>$cflow</code> variable
+ */
+ public Object[] lookupCflow(String name) {
+ if (cflow == null)
+ cflow = new Hashtable();
+
+ return (Object[])cflow.get(name);
+ }
+
+ /**
+ * Reads a class file and constructs a <code>CtClass</code>
+ * object with a new name.
+ * This method is useful if you want to generate a new class as a copy
+ * of another class (except the class name). For example,
+ *
+ * <ul><pre>
+ * getAndRename("Point", "Pair")
+ * </pre></ul>
+ *
+ * returns a <code>CtClass</code> object representing <code>Pair</code>
+ * class. The definition of <code>Pair</code> is the same as that of
+ * <code>Point</code> class except the class name since <code>Pair</code>
+ * is defined by reading <code>Point.class</code>.
+ *
+ * @param orgName the original (fully-qualified) class name
+ * @param newName the new class name
+ */
+ public CtClass getAndRename(String orgName, String newName)
+ throws NotFoundException
+ {
+ CtClass clazz = get0(orgName, false);
+ if (clazz instanceof CtClassType)
+ ((CtClassType)clazz).setClassPool(this);
+
+ clazz.setName(newName); // indirectly calls
+ // classNameChanged() in this class
+ return clazz;
+ }
+
+ /*
+ * This method is invoked by CtClassType.setName(). It removes a
+ * CtClass object from the hash table and inserts it with the new
+ * name. Don't delegate to the parent.
+ */
+ synchronized void classNameChanged(String oldname, CtClass clazz) {
+ CtClass c = (CtClass)getCached(oldname);
+ if (c == clazz) // must check this equation.
+ removeCached(oldname); // see getAndRename().
+
+ String newName = clazz.getName();
+ checkNotFrozen(newName);
+ cacheCtClass(newName, clazz);
+ }
+
+ /**
+ * Reads a class file from the source and returns a reference
+ * to the <code>CtClass</code>
+ * object representing that class file. If that class file has been
+ * already read, this method returns a reference to the
+ * <code>CtClass</code> created when that class file was read at the
+ * first time.
+ *
+ * <p>If <code>classname</code> ends with "[]", then this method
+ * returns a <code>CtClass</code> object for that array type.
+ *
+ * <p>To obtain an inner class, use "$" instead of "." for separating
+ * the enclosing class name and the inner class name.
+ *
+ * @param classname a fully-qualified class name.
+ */
+ public CtClass get(String classname) throws NotFoundException {
+ CtClass clazz;
+ if (classname == null)
+ clazz = null;
+ else
+ clazz = get0(classname, true);
+
+ if (clazz == null)
+ throw new NotFoundException(classname);
+ else {
+ clazz.incGetCounter();
return clazz;
- }
+ }
+ }
+
+ /**
+ * @param useCache false if the cached CtClass must be ignored.
+ * @param searchParent false if the parent class pool is not searched.
+ * @return null if the class could not be found.
+ */
+ protected synchronized CtClass get0(String classname, boolean useCache)
+ throws NotFoundException
+ {
+ CtClass clazz = null;
+ if (useCache) {
+ clazz = getCached(classname);
+ if (clazz != null)
+ return clazz;
+ }
+
+ if (!childFirstLookup && parent != null) {
+ clazz = parent.get0(classname, useCache);
+ if (clazz != null)
+ return clazz;
+ }
+
+ clazz = createCtClass(classname, useCache);
+ if (clazz != null) {
+ if (useCache)
+ cacheCtClass(classname, clazz);
- if (!childFirstLookup && parent != null)
- {
- clazz = parent.get0(classname, useCache);
- if (clazz != null)
return clazz;
- }
-
- clazz = createCtClass(classname, useCache);
- if (clazz != null)
- {
- if (useCache)
- cacheCtClass(classname, clazz);
-
- return clazz;
- }
-
- if (childFirstLookup && parent != null)
- clazz = parent.get0(classname, useCache);
-
- return clazz;
- }
-
- /**
- * Creates a CtClass object representing the specified class.
- * It first examines whether or not the corresponding class
- * file exists. If yes, it creates a CtClass object.
- *
- * @return null if the class file could not be found.
- */
- protected CtClass createCtClass(String classname, boolean useCache)
- {
- if (classname.endsWith("[]"))
- {
- String base = classname.substring(0, classname.indexOf('['));
- if ((!useCache || getCached(base) == null) && find(base) == null)
- return null;
- else
- return new CtArray(classname, this);
- }
- else if (find(classname) == null)
- return null;
- else
- return new CtClassType(classname, this);
- }
-
- /**
- * Searches the class path to obtain the URL of the class file
- * specified by classname. It is also used to determine whether
- * the class file exists.
- *
- * @param classname a fully-qualified class name.
- * @return null if the class file could not be found.
- * @see CtClass#getURL()
- */
- public URL find(String classname)
- {
- return source.find(classname);
- }
-
- /*
- * Is invoked by CtClassType.setName() and methods in this class.
- * This method throws an exception if the class is already frozen or
- * if this class pool cannot edit the class since it is in a parent
- * class pool.
- */
- void checkNotFrozen(String classname) throws RuntimeException
- {
- CtClass clazz = getCached(classname);
- if (clazz == null)
- {
- if (!childFirstLookup && parent != null)
- {
- try
- {
- clazz = parent.get0(classname, true);
+ }
+
+ if (childFirstLookup && parent != null)
+ clazz = parent.get0(classname, useCache);
+
+ return clazz;
+ }
+
+ /**
+ * Creates a CtClass object representing the specified class.
+ * It first examines whether or not the corresponding class
+ * file exists. If yes, it creates a CtClass object.
+ *
+ * @return null if the class file could not be found.
+ */
+ protected CtClass createCtClass(String classname, boolean useCache) {
+ if (classname.endsWith("[]")) {
+ String base = classname.substring(0, classname.indexOf('['));
+ if ((!useCache || getCached(base) == null) && find(base) == null)
+ return null;
+ else
+ return new CtArray(classname, this);
+ }
+ else
+ if (find(classname) == null)
+ return null;
+ else
+ return new CtClassType(classname, this);
+ }
+
+ /**
+ * Searches the class path to obtain the URL of the class file
+ * specified by classname. It is also used to determine whether
+ * the class file exists.
+ *
+ * @param classname a fully-qualified class name.
+ * @return null if the class file could not be found.
+ * @see CtClass#getURL()
+ */
+ public URL find(String classname) {
+ return source.find(classname);
+ }
+
+ /*
+ * Is invoked by CtClassType.setName() and methods in this class.
+ * This method throws an exception if the class is already frozen or
+ * if this class pool cannot edit the class since it is in a parent
+ * class pool.
+ */
+ void checkNotFrozen(String classname) throws RuntimeException {
+ CtClass clazz = getCached(classname);
+ if (clazz == null) {
+ if (!childFirstLookup && parent != null) {
+ try {
+ clazz = parent.get0(classname, true);
+ }
+ catch (NotFoundException e) {}
+ if (clazz != null)
+ throw new RuntimeException(classname
+ + " is in a parent ClassPool. Use the parent.");
}
- catch (NotFoundException e)
- {
+ }
+ else
+ if (clazz.isFrozen())
+ throw new RuntimeException(classname
+ + ": frozen class (cannot edit)");
+ }
+
+ /* for CtClassType.getClassFile2(). Don't delegate to the parent.
+ */
+ InputStream openClassfile(String classname) throws NotFoundException {
+ return source.openClassfile(classname);
+ }
+
+ void writeClassfile(String classname, OutputStream out)
+ throws NotFoundException, IOException, CannotCompileException
+ {
+ source.writeClassfile(classname, out);
+ }
+
+ /**
+ * Reads class files from the source and returns an array of
+ * <code>CtClass</code>
+ * objects representing those class files.
+ *
+ * <p>If an element of <code>classnames</code> ends with "[]",
+ * then this method
+ * returns a <code>CtClass</code> object for that array type.
+ *
+ * @param classnames an array of fully-qualified class name.
+ */
+ public CtClass[] get(String[] classnames) throws NotFoundException {
+ if (classnames == null)
+ return new CtClass[0];
+
+ int num = classnames.length;
+ CtClass[] result = new CtClass[num];
+ for (int i = 0; i < num; ++i)
+ result[i] = get(classnames[i]);
+
+ return result;
+ }
+
+ /**
+ * Reads a class file and obtains a compile-time method.
+ *
+ * @param classname the class name
+ * @param methodname the method name
+ * @see CtClass#getDeclaredMethod(String)
+ */
+ public CtMethod getMethod(String classname, String methodname)
+ throws NotFoundException
+ {
+ CtClass c = get(classname);
+ return c.getDeclaredMethod(methodname);
+ }
+
+ /**
+ * Creates a new class (or interface) from the given class file.
+ * If there already exists a class with the same name, the new class
+ * overwrites that previous class.
+ *
+ * <p>This method is used for creating a <code>CtClass</code> object
+ * directly from a class file. The qualified class name is obtained
+ * from the class file; you do not have to explicitly give the name.
+ *
+ * @param classfile class file.
+ * @throws RuntimeException if there is a frozen class with the
+ * the same name.
+ * @see javassist.ByteArrayClassPath
+ */
+ public CtClass makeClass(InputStream classfile)
+ throws IOException, RuntimeException {
+ classfile = new BufferedInputStream(classfile);
+ CtClass clazz = new CtClassType(classfile, this);
+ clazz.checkModify();
+ String classname = clazz.getName();
+ checkNotFrozen(classname);
+ cacheCtClass(classname, clazz);
+ return clazz;
+ }
+
+ /**
+ * Creates a new public class.
+ * If there already exists a class with the same name, the new class
+ * overwrites that previous class.
+ *
+ * @param classname a fully-qualified class name.
+ * @throws RuntimeException if the existing class is frozen.
+ */
+ public CtClass makeClass(String classname) throws RuntimeException {
+ return makeClass(classname, null);
+ }
+
+ /**
+ * Creates a new public class.
+ * If there already exists a class/interface with the same name,
+ * the new class overwrites that previous class.
+ *
+ * @param classname a fully-qualified class name.
+ * @param superclass the super class.
+ * @throws RuntimeException if the existing class is frozen.
+ */
+ public synchronized CtClass makeClass(String classname, CtClass superclass)
+ throws RuntimeException
+ {
+ checkNotFrozen(classname);
+ CtClass clazz = new CtNewClass(classname, this, false, superclass);
+ cacheCtClass(classname, clazz);
+ return clazz;
+ }
+
+ /**
+ * Creates a new nested class.
+ * This method is called by CtClassType.makeNestedClass().
+ *
+ * @param classname a fully-qualified class name.
+ * @return the nested class.
+ */
+ synchronized CtClass makeNestedClass(String classname) {
+ checkNotFrozen(classname);
+ CtClass clazz = new CtNewNestedClass(classname, this, false, null);
+ cacheCtClass(classname, clazz);
+ return clazz;
+ }
+
+ /**
+ * Creates a new public interface.
+ * If there already exists a class/interface with the same name,
+ * the new interface overwrites that previous one.
+ *
+ * @param name a fully-qualified interface name.
+ * @throws RuntimeException if the existing interface is frozen.
+ */
+ public CtClass makeInterface(String name) throws RuntimeException {
+ return makeInterface(name, null);
+ }
+
+ /**
+ * Creates a new public interface.
+ * If there already exists a class/interface with the same name,
+ * the new interface overwrites that previous one.
+ *
+ * @param name a fully-qualified interface name.
+ * @param superclass the super interface.
+ * @throws RuntimeException if the existing interface is frozen.
+ */
+ public synchronized CtClass makeInterface(String name, CtClass superclass)
+ throws RuntimeException
+ {
+ checkNotFrozen(name);
+ CtClass clazz = new CtNewClass(name, this, true, superclass);
+ cacheCtClass(name, clazz);
+ return clazz;
+ }
+
+ /**
+ * Appends the system search path to the end of the
+ * search path. The system search path
+ * usually includes the platform library, extension
+ * libraries, and the search path specified by the
+ * <code>-classpath</code> option or the <code>CLASSPATH</code>
+ * environment variable.
+ *
+ * @return the appended class path.
+ */
+ public ClassPath appendSystemPath() {
+ return source.appendSystemPath();
+ }
+
+ /**
+ * Insert a <code>ClassPath</code> object at the head of the
+ * search path.
+ *
+ * @return the inserted class path.
+ * @see javassist.ClassPath
+ * @see javassist.URLClassPath
+ * @see javassist.ByteArrayClassPath
+ */
+ public ClassPath insertClassPath(ClassPath cp) {
+ return source.insertClassPath(cp);
+ }
+
+ /**
+ * Appends a <code>ClassPath</code> object to the end of the
+ * search path.
+ *
+ * @return the appended class path.
+ * @see javassist.ClassPath
+ * @see javassist.URLClassPath
+ * @see javassist.ByteArrayClassPath
+ */
+ public ClassPath appendClassPath(ClassPath cp) {
+ return source.appendClassPath(cp);
+ }
+
+ /**
+ * Inserts a directory or a jar (or zip) file at the head of the
+ * search path.
+ *
+ * @param pathname the path name of the directory or jar file.
+ * It must not end with a path separator ("/").
+ * @return the inserted class path.
+ * @throws NotFoundException if the jar file is not found.
+ */
+ public ClassPath insertClassPath(String pathname)
+ throws NotFoundException
+ {
+ return source.insertClassPath(pathname);
+ }
+
+ /**
+ * Appends a directory or a jar (or zip) file to the end of the
+ * search path.
+ *
+ * @param pathname the path name of the directory or jar file.
+ * It must not end with a path separator ("/").
+ * @return the appended class path.
+ * @throws NotFoundException if the jar file is not found.
+ */
+ public ClassPath appendClassPath(String pathname)
+ throws NotFoundException
+ {
+ return source.appendClassPath(pathname);
+ }
+
+ /**
+ * Detatches the <code>ClassPath</code> object from the search path.
+ * The detached <code>ClassPath</code> object cannot be added
+ * to the pathagain.
+ */
+ public void removeClassPath(ClassPath cp) {
+ source.removeClassPath(cp);
+ }
+
+ /**
+ * Appends directories and jar files for search.
+ *
+ * <p>The elements of the given path list must be separated by colons
+ * in Unix or semi-colons in Windows.
+ *
+ * @param pathlist a (semi)colon-separated list of
+ * the path names of directories and jar files.
+ * The directory name must not end with a path
+ * separator ("/").
+ * @throws NotFoundException if a jar file is not found.
+ */
+ public void appendPathList(String pathlist) throws NotFoundException {
+ char sep = File.pathSeparatorChar;
+ int i = 0;
+ for (;;) {
+ int j = pathlist.indexOf(sep, i);
+ if (j < 0) {
+ appendClassPath(pathlist.substring(i));
+ break;
}
- if (clazz != null)
- throw new RuntimeException(classname
- + " is in a parent ClassPool. Use the parent.");
- }
- }
- else if (clazz.isFrozen())
- throw new RuntimeException(classname +
- ": frozen class (cannot edit)");
- }
-
- /* for CtClassType.getClassFile2(). Don't delegate to the parent.
- */
- InputStream openClassfile(String classname)
- throws NotFoundException
- {
- return source.openClassfile(classname);
- }
-
- void writeClassfile(String classname, OutputStream out)
- throws NotFoundException, IOException, CannotCompileException
- {
- source.writeClassfile(classname, out);
- }
-
- /**
- * Reads class files from the source and returns an array of
- * <code>CtClass</code>
- * objects representing those class files.
- * <p/>
- * <p>If an element of <code>classnames</code> ends with "[]",
- * then this method
- * returns a <code>CtClass</code> object for that array type.
- *
- * @param classnames an array of fully-qualified class name.
- */
- public CtClass[] get(String[] classnames) throws NotFoundException
- {
- if (classnames == null)
- return new CtClass[0];
-
- int num = classnames.length;
- CtClass[] result = new CtClass[num];
- for (int i = 0; i < num; ++i)
- result[i] = get(classnames[i]);
-
- return result;
- }
-
- /**
- * Reads a class file and obtains a compile-time method.
- *
- * @param classname the class name
- * @param methodname the method name
- * @see CtClass#getDeclaredMethod(String)
- */
- public CtMethod getMethod(String classname, String methodname)
- throws NotFoundException
- {
- CtClass c = get(classname);
- return c.getDeclaredMethod(methodname);
- }
-
- /**
- * Creates a new class (or interface) from the given class file.
- * If there already exists a class with the same name, the new class
- * overwrites that previous class.
- * <p/>
- * <p>This method is used for creating a <code>CtClass</code> object
- * directly from a class file. The qualified class name is obtained
- * from the class file; you do not have to explicitly give the name.
- *
- * @param classfile class file.
- * @throws RuntimeException if there is a frozen class with the
- * the same name.
- * @see javassist.ByteArrayClassPath
- */
- public CtClass makeClass(InputStream classfile)
- throws IOException, RuntimeException
- {
- classfile = new BufferedInputStream(classfile);
- CtClass clazz = new CtClassType(classfile, this);
- clazz.checkModify();
- String classname = clazz.getName();
- checkNotFrozen(classname);
- cacheCtClass(classname, clazz);
- return clazz;
- }
-
- /**
- * Creates a new public class.
- * If there already exists a class with the same name, the new class
- * overwrites that previous class.
- *
- * @param classname a fully-qualified class name.
- * @throws RuntimeException if the existing class is frozen.
- */
- public CtClass makeClass(String classname) throws RuntimeException
- {
- return makeClass(classname, null);
- }
-
- /**
- * Creates a new public class.
- * If there already exists a class/interface with the same name,
- * the new class overwrites that previous class.
- *
- * @param classname a fully-qualified class name.
- * @param superclass the super class.
- * @throws RuntimeException if the existing class is frozen.
- */
- public synchronized CtClass makeClass(String classname, CtClass superclass)
- throws RuntimeException
- {
- checkNotFrozen(classname);
- CtClass clazz = new CtNewClass(classname, this, false, superclass);
- cacheCtClass(classname, clazz);
- return clazz;
- }
-
- /**
- * Creates a new nested class.
- * This method is called by CtClassType.makeNestedClass().
- *
- * @param classname a fully-qualified class name.
- * @return the nested class.
- */
- synchronized CtClass makeNestedClass(String classname)
- {
- checkNotFrozen(classname);
- CtClass clazz = new CtNewNestedClass(classname, this, false, null);
- cacheCtClass(classname, clazz);
- return clazz;
- }
-
- /**
- * Creates a new public interface.
- * If there already exists a class/interface with the same name,
- * the new interface overwrites that previous one.
- *
- * @param name a fully-qualified interface name.
- * @throws RuntimeException if the existing interface is frozen.
- */
- public CtClass makeInterface(String name) throws RuntimeException
- {
- return makeInterface(name, null);
- }
-
- /**
- * Creates a new public interface.
- * If there already exists a class/interface with the same name,
- * the new interface overwrites that previous one.
- *
- * @param name a fully-qualified interface name.
- * @param superclass the super interface.
- * @throws RuntimeException if the existing interface is frozen.
- */
- public synchronized CtClass makeInterface(String name, CtClass superclass)
- throws RuntimeException
- {
- checkNotFrozen(name);
- CtClass clazz = new CtNewClass(name, this, true, superclass);
- cacheCtClass(name, clazz);
- return clazz;
- }
-
- /**
- * Appends the system search path to the end of the
- * search path. The system search path
- * usually includes the platform library, extension
- * libraries, and the search path specified by the
- * <code>-classpath</code> option or the <code>CLASSPATH</code>
- * environment variable.
- *
- * @return the appended class path.
- */
- public ClassPath appendSystemPath()
- {
- return source.appendSystemPath();
- }
-
- /**
- * Insert a <code>ClassPath</code> object at the head of the
- * search path.
- *
- * @return the inserted class path.
- * @see javassist.ClassPath
- * @see javassist.URLClassPath
- * @see javassist.ByteArrayClassPath
- */
- public ClassPath insertClassPath(ClassPath cp)
- {
- return source.insertClassPath(cp);
- }
-
- /**
- * Appends a <code>ClassPath</code> object to the end of the
- * search path.
- *
- * @return the appended class path.
- * @see javassist.ClassPath
- * @see javassist.URLClassPath
- * @see javassist.ByteArrayClassPath
- */
- public ClassPath appendClassPath(ClassPath cp)
- {
- return source.appendClassPath(cp);
- }
-
- /**
- * Inserts a directory or a jar (or zip) file at the head of the
- * search path.
- *
- * @param pathname the path name of the directory or jar file.
- * It must not end with a path separator ("/").
- * @return the inserted class path.
- * @throws NotFoundException if the jar file is not found.
- */
- public ClassPath insertClassPath(String pathname)
- throws NotFoundException
- {
- return source.insertClassPath(pathname);
- }
-
- /**
- * Appends a directory or a jar (or zip) file to the end of the
- * search path.
- *
- * @param pathname the path name of the directory or jar file.
- * It must not end with a path separator ("/").
- * @return the appended class path.
- * @throws NotFoundException if the jar file is not found.
- */
- public ClassPath appendClassPath(String pathname)
- throws NotFoundException
- {
- return source.appendClassPath(pathname);
- }
-
- /**
- * Detatches the <code>ClassPath</code> object from the search path.
- * The detached <code>ClassPath</code> object cannot be added
- * to the pathagain.
- */
- public void removeClassPath(ClassPath cp)
- {
- source.removeClassPath(cp);
- }
-
- /**
- * Appends directories and jar files for search.
- * <p/>
- * <p>The elements of the given path list must be separated by colons
- * in Unix or semi-colons in Windows.
- *
- * @param pathlist a (semi)colon-separated list of
- * the path names of directories and jar files.
- * The directory name must not end with a path
- * separator ("/").
- * @throws NotFoundException if a jar file is not found.
- */
- public void appendPathList(String pathlist) throws NotFoundException
- {
- char sep = File.pathSeparatorChar;
- int i = 0;
- for (; ;)
- {
- int j = pathlist.indexOf(sep, i);
- if (j < 0)
- {
- appendClassPath(pathlist.substring(i));
- break;
- }
- else
- {
- appendClassPath(pathlist.substring(i, j));
- i = j + 1;
- }
- }
- }
-
- /**
- * Converts this class to a <code>java.lang.Class</code> object.
- * Once this method is called, further modifications are not
- * allowed any more.
- * To load the class, this method uses the context class loader
- * of the current thread. If the program is running on some application
- * server, the context class loader might be inappropriate to load the
- * class.
- * <p/>
- * <p>This method is provided for convenience. If you need more
- * complex functionality, you should write your own class loader.
- *
- * @see #toClass(java.lang.ClassLoader)
- */
- public Class toClass(CtClass clazz)
- throws CannotCompileException
- {
- return toClass(clazz, Thread.currentThread().getContextClassLoader());
- }
-
- /**
- * Converts this class to a <code>java.lang.Class</code> object.
- * Once this method is called, further modifications are not allowed
- * any more.
- * <p/>
- * <p>The class file represented by this <code>CtClass</code> is
- * loaded by the given class loader to construct a
- * <code>java.lang.Class</code> object. Since a private method
- * on the class loader is invoked through the reflection API,
- * the caller must have permissions to do that.
- * <p/>
- * <p>This method is provided for convenience. If you need more
- * complex functionality, you should write your own class loader.
- *
- * @param loader the class loader used to load this class.
- */
- public Class toClass(CtClass ct, ClassLoader loader)
- throws CannotCompileException
- {
- try
- {
- byte[] b = ct.toBytecode();
- Class cl = Class.forName("java.lang.ClassLoader");
- java.lang.reflect.Method method
- = cl.getDeclaredMethod("defineClass",
- new Class[]{String.class, byte[].class,
- int.class, int.class});
- method.setAccessible(true);
- Object[] args = new Object[]{ct.getName(), b, new Integer(0),
- new Integer(b.length)};
- Class clazz = (Class) method.invoke(loader, args);
- method.setAccessible(false);
- return clazz;
- }
- catch (RuntimeException e)
- {
- throw e;
- }
- catch (java.lang.reflect.InvocationTargetException e)
- {
- throw new CannotCompileException(e.getTargetException());
- }
- catch (Exception e)
- {
- throw new CannotCompileException(e);
- }
- }
-
+ else {
+ appendClassPath(pathlist.substring(i, j));
+ i = j + 1;
+ }
+ }
+ }
+
+ /**
+ * Converts the given class to a <code>java.lang.Class</code> object.
+ * Once this method is called, further modifications are not
+ * allowed any more.
+ * To load the class, this method uses the context class loader
+ * of the current thread. If the program is running on some application
+ * server, the context class loader might be inappropriate to load the
+ * class.
+ *
+ * <p>This method is provided for convenience. If you need more
+ * complex functionality, you should write your own class loader.
+ *
+ * @see #toClass(CtClass, java.lang.ClassLoader)
+ */
+ public Class toClass(CtClass clazz) throws CannotCompileException {
+ return toClass(clazz, Thread.currentThread().getContextClassLoader());
+ }
+
+ /**
+ * Converts the class to a <code>java.lang.Class</code> object.
+ * Once this method is called, further modifications are not allowed
+ * any more.
+ *
+ * <p>The class file represented by the given <code>CtClass</code> is
+ * loaded by the given class loader to construct a
+ * <code>java.lang.Class</code> object. Since a private method
+ * on the class loader is invoked through the reflection API,
+ * the caller must have permissions to do that.
+ *
+ * <p>This method is provided for convenience. If you need more
+ * complex functionality, you should write your own class loader.
+ *
+ * @param loader the class loader used to load this class.
+ */
+ public Class toClass(CtClass ct, ClassLoader loader)
+ throws CannotCompileException
+ {
+ try {
+ byte[] b = ct.toBytecode();
+ Class cl = Class.forName("java.lang.ClassLoader");
+ java.lang.reflect.Method method =
+ cl.getDeclaredMethod("defineClass",
+ new Class[] { String.class, byte[].class,
+ int.class, int.class });
+ method.setAccessible(true);
+ Object[] args = new Object[] { ct.getName(), b, new Integer(0),
+ new Integer(b.length)};
+ Class clazz = (Class)method.invoke(loader, args);
+ method.setAccessible(false);
+ return clazz;
+ }
+ catch (RuntimeException e) {
+ throw e;
+ }
+ catch (java.lang.reflect.InvocationTargetException e) {
+ throw new CannotCompileException(e.getTargetException());
+ }
+ catch (Exception e) {
+ throw new CannotCompileException(e);
+ }
+ }
}
-
*
* @see ClassPool#get(String)
*/
-public abstract class CtClass
-{
- protected String qualifiedName;
-
- /**
- * The version number of this release.
- */
- public static final String version = "3.0 RC1";
-
- /**
- * Prints the version number and the copyright notice.
- * <p/>
- * <p>The following command invokes this method:
- * <p/>
- * <ul><pre>java -jar javassist.jar</pre></ul>
- */
- public static void main(String[] args)
- {
- System.out.println("Javassist version " + CtClass.version);
- System.out.println("Copyright (C) 1999-2004 Shigeru Chiba."
- + " All Rights Reserved.");
- }
-
- static final String javaLangObject = "java.lang.Object";
-
- /**
- * The <code>CtClass</code> object representing
- * the <code>boolean</code> type.
- */
- public static CtClass booleanType;
-
- /**
- * The <code>CtClass</code> object representing
- * the <code>char</code> type.
- */
- public static CtClass charType;
-
- /**
- * The <code>CtClass</code> object representing
- * the <code>byte</code> type.
- */
- public static CtClass byteType;
-
- /**
- * The <code>CtClass</code> object representing
- * the <code>short</code> type.
- */
- public static CtClass shortType;
-
- /**
- * The <code>CtClass</code> object representing
- * the <code>int</code> type.
- */
- public static CtClass intType;
-
- /**
- * The <code>CtClass</code> object representing
- * the <code>long</code> type.
- */
- public static CtClass longType;
-
- /**
- * The <code>CtClass</code> object representing
- * the <code>float</code> type.
- */
- public static CtClass floatType;
-
- /**
- * The <code>CtClass</code> object representing
- * the <code>double</code> type.
- */
- public static CtClass doubleType;
-
- /**
- * The <code>CtClass</code> object representing
- * the <code>void</code> type.
- */
- public static CtClass voidType;
-
- static CtClass[] primitiveTypes;
-
- static
- {
- primitiveTypes = new CtClass[9];
-
- booleanType = new CtPrimitiveType("boolean", 'Z', "java.lang.Boolean",
- "booleanValue", "()Z", Opcode.IRETURN,
- Opcode.T_BOOLEAN, 1);
- primitiveTypes[0] = booleanType;
-
- charType = new CtPrimitiveType("char", 'C', "java.lang.Character",
- "charValue", "()C", Opcode.IRETURN,
- Opcode.T_CHAR, 1);
- primitiveTypes[1] = charType;
-
- byteType = new CtPrimitiveType("byte", 'B', "java.lang.Byte",
- "byteValue", "()B", Opcode.IRETURN,
- Opcode.T_BYTE, 1);
- primitiveTypes[2] = byteType;
-
- shortType = new CtPrimitiveType("short", 'S', "java.lang.Short",
- "shortValue", "()S", Opcode.IRETURN,
- Opcode.T_SHORT, 1);
- primitiveTypes[3] = shortType;
-
- intType = new CtPrimitiveType("int", 'I', "java.lang.Integer",
- "intValue", "()I", Opcode.IRETURN,
- Opcode.T_INT, 1);
- primitiveTypes[4] = intType;
-
- longType = new CtPrimitiveType("long", 'J', "java.lang.Long",
- "longValue", "()J", Opcode.LRETURN,
- Opcode.T_LONG, 2);
- primitiveTypes[5] = longType;
-
- floatType = new CtPrimitiveType("float", 'F', "java.lang.Float",
- "floatValue", "()F", Opcode.FRETURN,
- Opcode.T_FLOAT, 1);
- primitiveTypes[6] = floatType;
-
- doubleType = new CtPrimitiveType("double", 'D', "java.lang.Double",
- "doubleValue", "()D", Opcode.DRETURN,
- Opcode.T_DOUBLE, 2);
- primitiveTypes[7] = doubleType;
-
- voidType = new CtPrimitiveType("void", 'V', "java.lang.Void",
- null, null, Opcode.RETURN, 0, 0);
- primitiveTypes[8] = voidType;
- }
-
- protected CtClass(String name)
- {
- qualifiedName = name;
- }
-
- /**
- * Converts the object to a string.
- */
- public String toString()
- {
- StringBuffer buf = new StringBuffer(getClass().getName());
- buf.append("@");
- buf.append(Integer.toHexString(hashCode()));
- buf.append("[");
- extendToString(buf);
- buf.append("]");
- return buf.toString();
- }
-
- /**
- * Implemented in subclasses to add to the {@link #toString()} result.
- * Subclasses should put a space before each token added to the buffer.
- */
- protected void extendToString(StringBuffer buffer)
- {
- buffer.append(getName());
- }
-
- /**
- * Returns a <code>ClassPool</code> for this class.
- */
- public ClassPool getClassPool()
- {
- return null;
- }
-
- /**
- * Returns a class file for this class.
- * <p/>
- * <p>This method is not available if <code>isFrozen()</code>
- * is true.
- */
- public ClassFile getClassFile()
- {
- checkModify();
- return getClassFile2();
- }
-
- /**
- * Undocumented method. Do not use; internal-use only.
- */
- public ClassFile getClassFile2()
- {
- return null;
- }
-
- /**
- * Undocumented method. Do not use; internal-use only.
- */
- public javassist.compiler.AccessorMaker getAccessorMaker()
- {
- return null;
- }
-
- /**
- * Returns the uniform resource locator (URL) of the class file.
- */
- public URL getURL() throws NotFoundException
- {
- throw new NotFoundException(getName());
- }
-
- /**
- * Returns true if the definition of the class has been modified.
- */
- public boolean isModified()
- {
- return false;
- }
-
- /**
- * Returns true if the class has been loaded or written out
- * and thus it cannot be modified any more.
- *
- * @see #defrost()
- */
- public boolean isFrozen()
- {
- return true;
- }
-
- void freeze()
- {
- }
-
- void checkModify() throws RuntimeException
- {
- if (isFrozen())
- throw new RuntimeException(getName() + " class is frozen");
-
- // isModified() must return true after this method is invoked.
- }
-
- /**
- * Defrosts the class so that the class can be modified again.
- * <p/>
- * <p>To avoid changes that will be never reflected,
- * the class is frozen to be unmodifiable if it is loaded or
- * written out. This method should be called only in a case
- * that the class will be reloaded or written out later again.
- * <p/>
- * <p>If <code>defrost()</code> will be called later, pruning
- * must be disallowed in advance.
- *
- * @see #isFrozen()
- * @see #stopPruning(boolean)
- */
- public void defrost()
- {
- throw new RuntimeException("cannot defrost " + getName());
- }
-
- /**
- * Disallows (or allows) pruning the data structure on memory
- * when this <code>CtClass</code> object is converted into a class file.
- * Pruning saves memory space since a <code>ClassPool</code> holds
- * all instances of <code>CtClass</code>
- * all the time of program execution.
- * However, pruning discards the data representing part of the
- * class definition, such as method bodies.
- * Therefore, once it is pruned, <code>toBytecode()</code>,
- * <code>writeFile()</code>, or <code>toClass()</code> cannot
- * be called again.
- * <p/>
- * <p>Initially, pruning is allowed.
- *
- * @param stop disallow pruning if true. Otherwise, allow.
- * @see #detach()
- * @see #toBytecode()
- * @see #toClass()
- * @see #writeFile()
- */
- public void stopPruning(boolean stop)
- {
- }
-
- /* Called by get() in ClassPool.
- * CtClassType overrides this method.
- */
- void incGetCounter()
- {
- }
-
- /**
- * Returns <code>true</code> if this object represents a primitive
- * Java type: boolean, byte, char, short, int, long, float, double,
- * or void.
- */
- public boolean isPrimitive()
- {
- return false;
- }
-
- /**
- * Returns <code>true</code> if this object represents an array type.
- */
- public boolean isArray()
- {
- return false;
- }
-
- /**
- * If this object represents an array, this method returns the component
- * type of the array. Otherwise, it returns <code>null</code>.
- */
- public CtClass getComponentType() throws NotFoundException
- {
- return null;
- }
-
- /**
- * Returns <code>true</code> if this class extends or implements
- * <code>clazz</code>. It also returns <code>true</code> if
- * this class is the same as <code>clazz</code>.
- */
- public boolean subtypeOf(CtClass clazz) throws NotFoundException
- {
- return this == clazz || getName().equals(clazz.getName());
- }
-
- /**
- * Obtains the fully-qualified name of the class.
- */
- public String getName()
- {
- return qualifiedName;
- }
-
- /**
- * Obtains the not-qualified class name.
- */
- public final String getSimpleName()
- {
- String qname = qualifiedName;
- int index = qname.lastIndexOf('.');
- if (index < 0)
- return qname;
- else
- return qname.substring(index + 1);
- }
-
- /**
- * Obtains the package name. It may be <code>null</code>.
- */
- public final String getPackageName()
- {
- String qname = qualifiedName;
- int index = qname.lastIndexOf('.');
- if (index < 0)
- return null;
- else
- return qname.substring(0, index);
- }
-
- /**
- * Sets the class name
- *
- * @param name fully-qualified name
- */
- public void setName(String name)
- {
- checkModify();
- if (name != null)
- qualifiedName = name;
- }
-
- /**
- * Substitutes <code>newName</code> for all occurrences of a class
- * name <code>oldName</code> in the class file.
- *
- * @param oldName replaced class name
- * @param newName substituted class name
- */
- public void replaceClassName(String oldName, String newName)
- {
- checkModify();
- }
-
- /**
- * Changes class names appearing in the class file according to the
- * given <code>map</code>.
- * <p/>
- * <p>All the class names appearing in the class file are tested
- * with <code>map</code> to determine whether each class name is
- * replaced or not. Thus this method can be used for collecting
- * all the class names in the class file. To do that, first define
- * a subclass of <code>ClassMap</code> so that <code>get()</code>
- * records all the given parameters. Then, make an instance of
- * that subclass as an empty hash-table. Finally, pass that instance
- * to this method. After this method finishes, that instance would
- * contain all the class names appearing in the class file.
- *
- * @param map the hashtable associating replaced class names
- * with substituted names.
- */
- public void replaceClassName(ClassMap map)
- {
- checkModify();
- }
-
- /**
- * Returns a collection of the names of all the classes
- * referenced in this class.
- * That collection includes the name of this class.
- * <p/>
- * <p>This method may return <code>null</code>.
- */
- public Collection getRefClasses()
- {
- ClassFile cf = getClassFile2();
- if (cf != null)
- {
- ClassMap cm = new ClassMap()
- {
- public void put(String oldname, String newname)
- {
- put0(oldname, newname);
- }
-
- public Object get(Object jvmClassName)
- {
- String n = toJavaName((String) jvmClassName);
- put0(n, n);
- return null;
- }
-
- public void fix(String name)
- {
- }
- };
- cf.renameClass(cm);
- return cm.values();
- }
- else
- return null;
- }
-
- /**
- * Determines whether this object represents a class or an interface.
- * It returns <code>true</code> if this object represents an interface.
- */
- public boolean isInterface()
- {
- return false;
- }
-
- /**
- * Returns the modifiers for this class, encoded in an integer.
- * For decoding, use <code>javassist.Modifier</code>.
- *
- * @see Modifier
- */
- public int getModifiers()
- {
- return 0;
- }
-
- /**
- * Sets the modifiers.
- *
- * @param mod modifiers encoded by
- * <code>javassist.Modifier</code>
- * @see Modifier
- */
- public void setModifiers(int mod)
- {
- checkModify();
- }
-
- /**
- * Determines whether the class directly or indirectly extends
- * the given class. If this class extends a class A and
- * the class A extends a class B, then subclassof(B) returns true.
- * <p/>
- * <p>This method returns true if the given class is identical to
- * the class represented by this object.
- */
- public boolean subclassOf(CtClass superclass)
- {
- return false;
- }
-
- /**
- * Obtains the class object representing the superclass of the
- * class.
- * It returns null if this object represents the
- * <code>java.lang.Object</code> class and thus it does not have
- * the super class.
- * <p/>
- * <p>If this object represents an interface, this method
- * always returns the <code>java.lang.Object</code> class.
- * To obtain the super interfaces
- * extended by that interface, call <code>getInterfaces()</code>.
- */
- public CtClass getSuperclass() throws NotFoundException
- {
- return null;
- }
-
- /**
- * Changes a super class unless this object represents an interface.
- * The new super class must be compatible with the old one.
- * <p/>
- * <p>If this object represents an interface, this method is equivalent
- * to <code>addInterface()</code>; it appends <code>clazz</code> to
- * the list of the super interfaces extended by that interface.
- * Note that an interface can extend multiple super interfaces.
- */
- public void setSuperclass(CtClass clazz) throws CannotCompileException
- {
- checkModify();
- }
-
- /**
- * Obtains the class objects representing the interfaces implemented
- * by the class or, if this object represents an interface, the interfaces
- * extended by that interface.
- */
- public CtClass[] getInterfaces() throws NotFoundException
- {
- return new CtClass[0];
- }
-
- /**
- * Sets implemented interfaces. If this object represents an interface,
- * this method sets the interfaces extended by that interface.
- *
- * @param list a list of the <code>CtClass</code> objects
- * representing interfaces, or
- * <code>null</code> if the class implements
- * no interfaces.
- */
- public void setInterfaces(CtClass[] list)
- {
- checkModify();
- }
-
- /**
- * Adds an interface.
- *
- * @param anInterface the added interface.
- */
- public void addInterface(CtClass anInterface)
- {
- checkModify();
- }
-
- /**
- * If this class is a member class or interface of another class,
- * then the class enclosing this class is returned.
- *
- * @return null if this class is a top-level class.
- */
- public CtClass getDeclaringClass() throws NotFoundException
- {
- return null;
- }
-
- /**
- * Makes a new nested class. Making a nested class modifies the
- * data in this <code>CtClass</code>.
- * <p/>
- * <p>The current implementation only supports a static nested class.
- * <code>isStatic</code> must be true.
- *
- * @param name the simple name of the nested class.
- * @param isStatic true if the nested class is static.
- */
- public CtClass makeNestedClass(String name, boolean isStatic)
- {
- throw new RuntimeException(getName() + " is not a class");
- }
-
- /**
- * Returns an array containing <code>CtField</code> objects
- * representing all the public fields of the class.
- * That array includes public fields inherited from the
- * superclasses.
- */
- public CtField[] getFields()
- {
- return new CtField[0];
- }
-
- /**
- * Returns the field with the specified name. The returned field
- * may be a private field declared in a super class or interface.
- */
- public CtField getField(String name) throws NotFoundException
- {
- throw new NotFoundException(name);
- }
-
- /**
- * @return null if the specified field is not found.
- */
- CtField getField2(String name)
- {
- return null;
- }
-
- /**
- * Gets all the fields declared in the class. The inherited fields
- * are not included.
- * <p/>
- * <p>Note: the result does not include inherited fields.
- */
- public CtField[] getDeclaredFields()
- {
- return new CtField[0];
- }
-
- /**
- * Retrieves the field with the specified name among the fields
- * declared in the class.
- * <p/>
- * <p>Note: this method does not search the superclasses.
- */
- public CtField getDeclaredField(String name) throws NotFoundException
- {
- throw new NotFoundException(name);
- }
-
- /**
- * Gets all the constructors and methods declared in the class.
- */
- public CtBehavior[] getDeclaredBehaviors()
- {
- return new CtBehavior[0];
- }
-
- /**
- * Returns an array containing <code>CtConstructor</code> objects
- * representing all the public constructors of the class.
- */
- public CtConstructor[] getConstructors()
- {
- return new CtConstructor[0];
- }
-
- /**
- * Returns the constructor with the given signature,
- * which is represented by a character string
- * called method descriptor.
- * For details of the method descriptor, see the JVM specification
- * or <code>javassist.bytecode.Descriptor</code>.
- *
- * @param desc method descriptor
- * @see javassist.bytecode.Descriptor
- */
- public CtConstructor getConstructor(String desc)
- throws NotFoundException
- {
- throw new NotFoundException("no such a constructor");
- }
-
- /**
- * Gets all the constructors declared in the class.
- *
- * @see javassist.CtConstructor
- */
- public CtConstructor[] getDeclaredConstructors()
- {
- return new CtConstructor[0];
- }
-
- /**
- * Returns a constructor receiving the specified parameters.
- *
- * @param params parameter types.
- */
- public CtConstructor getDeclaredConstructor(CtClass[] params)
- throws NotFoundException
- {
- String desc = Descriptor.ofConstructor(params);
- return getConstructor(desc);
- }
-
- /**
- * Gets the class initializer (static constructor)
- * declared in the class.
- * This method returns <code>null</code> if
- * no class initializer is not declared.
- *
- * @see #makeClassInitializer()
- * @see javassist.CtConstructor
- */
- public CtConstructor getClassInitializer()
- {
- return null;
- }
-
- /**
- * Returns an array containing <code>CtMethod</code> objects
- * representing all the public methods of the class.
- * That array includes public methods inherited from the
- * superclasses.
- */
- public CtMethod[] getMethods()
- {
- return new CtMethod[0];
- }
-
- /**
- * Returns the method with the given name and signature.
- * The returned method may be declared in a super class.
- * The method signature is represented by a character string
- * called method descriptor,
- * which is defined in the JVM specification.
- *
- * @param name method name
- * @param desc method descriptor
- * @see javassist.bytecode.Descriptor
- */
- public CtMethod getMethod(String name, String desc)
- throws NotFoundException
- {
- throw new NotFoundException(name);
- }
-
- /**
- * Gets all methods declared in the class. The inherited methods
- * are not included.
- *
- * @see javassist.CtMethod
- */
- public CtMethod[] getDeclaredMethods()
- {
- return new CtMethod[0];
- }
-
- /**
- * Retrieves the method with the specified name and parameter types
- * among the methods declared in the class.
- * <p/>
- * <p>Note: this method does not search the superclasses.
- *
- * @param name method name
- * @param params parameter types
- * @see javassist.CtMethod
- */
- public CtMethod getDeclaredMethod(String name, CtClass[] params)
- throws NotFoundException
- {
- throw new NotFoundException(name);
- }
-
- /**
- * Retrieves the method with the specified name among the methods
- * declared in the class. If there are multiple methods with
- * the specified name, then this method returns one of them.
- * <p/>
- * <p>Note: this method does not search the superclasses.
- *
- * @see javassist.CtMethod
- */
- public CtMethod getDeclaredMethod(String name) throws NotFoundException
- {
- throw new NotFoundException(name);
- }
-
- /**
- * Makes an empty class initializer (static constructor).
- * If the class already includes a class initializer,
- * this method returns it.
- *
- * @see #getClassInitializer()
- */
- public CtConstructor makeClassInitializer()
- throws CannotCompileException
- {
- throw new CannotCompileException("not a class");
- }
-
- /**
- * Adds a constructor. To add a class initializer (static constructor),
- * call <code>makeClassInitializer()</code>.
- *
- * @see #makeClassInitializer()
- */
- public void addConstructor(CtConstructor c)
- throws CannotCompileException
- {
- checkModify();
- }
-
- /**
- * Adds a method.
- */
- public void addMethod(CtMethod m) throws CannotCompileException
- {
- checkModify();
- }
-
- /**
- * Adds a field.
- * <p/>
- * <p>The <code>CtField</code> belonging to another
- * <code>CtClass</code> cannot be directly added to this class.
- * Only a field created for this class can be added.
- *
- * @see javassist.CtField#CtField(CtField,CtClass)
- */
- public void addField(CtField f) throws CannotCompileException
- {
- addField(f, (CtField.Initializer) null);
- }
-
- /**
- * Adds a field with an initial value.
- * <p/>
- * <p>The <code>CtField</code> belonging to another
- * <code>CtClass</code> cannot be directly added to this class.
- * Only a field created for this class can be added.
- * <p/>
- * <p>The initial value is given as an expression written in Java.
- * Any regular Java expression can be used for specifying the initial
- * value. The followings are examples.
- * <p/>
- * <ul><pre>
- * cc.addField(f, "0") // the initial value is 0.
- * cc.addField(f, "i + 1") // i + 1.
- * cc.addField(f, "new Point()"); // a Point object.
- * </pre></ul>
- * <p/>
- * <p>Here, the type of variable <code>cc</code> is <code>CtClass</code>.
- * The type of <code>f</code> is <code>CtField</code>.
- *
- * @param init an expression for the initial value.
- * @see javassist.CtField.Initializer#byExpr(String)
- * @see javassist.CtField#CtField(CtField,CtClass)
- */
- public void addField(CtField f, String init)
- throws CannotCompileException
- {
- checkModify();
- }
-
- /**
- * Adds a field with an initial value.
- * <p/>
- * <p>The <code>CtField</code> belonging to another
- * <code>CtClass</code> cannot be directly added to this class.
- * Only a field created for this class can be added.
- * <p/>
- * <p>For example,
- * <p/>
- * <ul><pre>
- * CtClass cc = ...;
- * addField(new CtField(CtClass.intType, "i", cc),
- * CtField.Initializer.constant(1));
- * </pre></ul>
- * <p/>
- * <p>This code adds an <code>int</code> field named "i". The
- * initial value of this field is 1.
- *
- * @param init specifies the initial value of the field.
- * @see javassist.CtField#CtField(CtField,CtClass)
- */
- public void addField(CtField f, CtField.Initializer init)
- throws CannotCompileException
- {
- checkModify();
- }
-
- /**
- * Obtains an attribute with the given name.
- * If that attribute is not found in the class file, this
- * method returns null.
- * <p/>
- * <p>This is a convenient method mainly for obtaining
- * a user-defined attribute. For dealing with attributes, see the
- * <code>javassist.bytecode</code> package. For example, the following
- * expression returns all the attributes of a class file.
- * <p/>
- * <ul><pre>
- * getClassFile().getAttributes()
- * </pre></ul>
- *
- * @param name attribute name
- * @see javassist.bytecode.AttributeInfo
- */
- public byte[] getAttribute(String name)
- {
- return null;
- }
-
- /**
- * Adds a named attribute.
- * An arbitrary data (smaller than 64Kb) can be saved in the class
- * file. Some attribute name are reserved by the JVM.
- * The attributes with the non-reserved names are ignored when a
- * class file is loaded into the JVM.
- * If there is already an attribute with
- * the same name, this method substitutes the new one for it.
- * <p/>
- * <p>This is a convenient method mainly for adding
- * a user-defined attribute. For dealing with attributes, see the
- * <code>javassist.bytecode</code> package. For example, the following
- * expression adds an attribute of a class file.
- * <p/>
- * <ul><pre>
- * getClassFile().addAttribute(info)
- * </pre></ul>
- *
- * @param name attribute name
- * @param data attribute value
- * @see javassist.bytecode.AttributeInfo
- */
- public void setAttribute(String name, byte[] data)
- {
- checkModify();
- }
-
- /**
- * Applies the given converter to all methods and constructors
- * declared in the class. This method calls <code>instrument()</code>
- * on every <code>CtMethod</code> and <code>CtConstructor</code> object
- * in the class.
- *
- * @param converter specifies how to modify.
- */
- public void instrument(CodeConverter converter)
- throws CannotCompileException
- {
- checkModify();
- }
-
- /**
- * Modifies the bodies of all methods and constructors
- * declared in the class. This method calls <code>instrument()</code>
- * on every <code>CtMethod</code> and <code>CtConstructor</code> object
- * in the class.
- *
- * @param editor specifies how to modify.
- */
- public void instrument(ExprEditor editor)
- throws CannotCompileException
- {
- checkModify();
- }
-
- /**
- * Converts this class to a <code>java.lang.Class</code> object.
- * Once this method is called, further modifications are not
- * allowed any more.
- * To load the class, this method uses the context class loader
- * of the current thread. If the program is running on some application
- * server, the context class loader might be inappropriate to load the
- * class.
- * <p/>
- * <p>This method is provided for convenience. If you need more
- * complex functionality, you should write your own class loader.
- *
- * @see #toClass(java.lang.ClassLoader)
- */
- public Class toClass()
- throws CannotCompileException
- {
- return getClassPool().toClass(this, Thread.currentThread().getContextClassLoader());
- }
-
- /**
- * Converts this class to a <code>java.lang.Class</code> object.
- * Once this method is called, further modifications are not allowed
- * any more.
- * <p/>
- * <p>The class file represented by this <code>CtClass</code> is
- * loaded by the given class loader to construct a
- * <code>java.lang.Class</code> object. Since a private method
- * on the class loader is invoked through the reflection API,
- * the caller must have permissions to do that.
- * <p/>
- * <p>This method is provided for convenience. If you need more
- * complex functionality, you should write your own class loader.
- *
- * @param loader the class loader used to load this class.
- */
- public Class toClass(ClassLoader loader)
- throws CannotCompileException
- {
- return getClassPool().toClass(this, loader);
- }
-
- /**
- * Removes this <code>CtClass</code> object from the
- * <code>ClassPool</code>.
- * After this method is called, any method cannot be called on the
- * removed <code>CtClass</code> object.
- * <p/>
- * <p>If <code>get()</code> in <code>ClassPool</code> is called
- * with the name of the removed method,
- * the <code>ClassPool</code> will read the class file again
- * and constructs another <code>CtClass</code> object representing
- * the same class.
- */
- public void detach()
- {
- ClassPool cp = getClassPool();
- CtClass obj = cp.removeCached(getName());
- if (obj != this)
- cp.cacheCtClass(getName(), obj);
- }
-
- /**
- * Converts this class to a class file.
- * Once this method is called, further modifications are not
- * possible any more.
- *
- * @return the contents of the class file.
- */
- public byte[] toBytecode()
- throws IOException, CannotCompileException
- {
- ByteArrayOutputStream barray = new ByteArrayOutputStream();
- DataOutputStream out = new DataOutputStream(barray);
- try
- {
- toBytecode(out);
- }
- finally
- {
- out.close();
- }
-
- return barray.toByteArray();
- }
-
- /**
- * Writes a class file represented by this <code>CtClass</code>
- * object in the current directory.
- * Once this method is called, further modifications are not
- * possible any more.
- */
- public void writeFile()
- throws NotFoundException, IOException, CannotCompileException
- {
- writeFile(".");
- }
-
- /**
- * Writes a class file represented by this <code>CtClass</code>
- * object on a local disk.
- * Once this method is called, further modifications are not
- * possible any more.
- *
- * @param directoryName it must end without a directory separator.
- */
- public void writeFile(String directoryName)
- throws NotFoundException, CannotCompileException, IOException
- {
- String classname = getName();
- String filename = directoryName + File.separatorChar
- + classname.replace('.', File.separatorChar) + ".class";
- int pos = filename.lastIndexOf(File.separatorChar);
- if (pos > 0)
- {
- String dir = filename.substring(0, pos);
- if (!dir.equals("."))
- new File(dir).mkdirs();
- }
-
- DataOutputStream out
- = new DataOutputStream(new BufferedOutputStream(new DelayedFileOutputStream(filename)));
- try
- {
- toBytecode(out);
- }
- finally
- {
- out.close();
- }
- }
-
- static class DelayedFileOutputStream extends OutputStream
- {
- private FileOutputStream file;
- private String filename;
-
- DelayedFileOutputStream(String name)
- {
- file = null;
- filename = name;
- }
-
- private void init() throws IOException
- {
- if (file == null)
- file = new FileOutputStream(filename);
- }
-
- public void write(int b) throws IOException
- {
- init();
- file.write(b);
- }
-
- public void write(byte[] b) throws IOException
- {
- init();
- file.write(b);
- }
-
- public void write(byte[] b, int off, int len) throws IOException
- {
- init();
- file.write(b, off, len);
-
- }
-
- public void flush() throws IOException
- {
- init();
- file.flush();
- }
-
- public void close() throws IOException
- {
- init();
- file.close();
- }
- }
-
- /**
- * Converts this class to a class file.
- * Once this method is called, further modifications are not
- * possible any more.
- * <p/>
- * <p>This method dose not close the output stream in the end.
- *
- * @param out the output stream that a class file is written to.
- */
- public void toBytecode(DataOutputStream out)
- throws CannotCompileException, IOException
- {
- throw new CannotCompileException("not a class");
- }
+public abstract class CtClass {
+ protected String qualifiedName;
+
+ /**
+ * The version number of this release.
+ */
+ public static final String version = "3.0 RC1";
+
+ /**
+ * Prints the version number and the copyright notice.
+ *
+ * <p>The following command invokes this method:
+ *
+ * <ul><pre>java -jar javassist.jar</pre></ul>
+ */
+ public static void main(String[] args) {
+ System.out.println("Javassist version " + CtClass.version);
+ System.out.println("Copyright (C) 1999-2004 Shigeru Chiba."
+ + " All Rights Reserved.");
+ }
+
+ static final String javaLangObject = "java.lang.Object";
+
+ /**
+ * The <code>CtClass</code> object representing
+ * the <code>boolean</code> type.
+ */
+ public static CtClass booleanType;
+
+ /**
+ * The <code>CtClass</code> object representing
+ * the <code>char</code> type.
+ */
+ public static CtClass charType;
+
+ /**
+ * The <code>CtClass</code> object representing
+ * the <code>byte</code> type.
+ */
+ public static CtClass byteType;
+
+ /**
+ * The <code>CtClass</code> object representing
+ * the <code>short</code> type.
+ */
+ public static CtClass shortType;
+
+ /**
+ * The <code>CtClass</code> object representing
+ * the <code>int</code> type.
+ */
+ public static CtClass intType;
+
+ /**
+ * The <code>CtClass</code> object representing
+ * the <code>long</code> type.
+ */
+ public static CtClass longType;
+
+ /**
+ * The <code>CtClass</code> object representing
+ * the <code>float</code> type.
+ */
+ public static CtClass floatType;
+
+ /**
+ * The <code>CtClass</code> object representing
+ * the <code>double</code> type.
+ */
+ public static CtClass doubleType;
+
+ /**
+ * The <code>CtClass</code> object representing
+ * the <code>void</code> type.
+ */
+ public static CtClass voidType;
+
+ static CtClass[] primitiveTypes;
+
+ static {
+ primitiveTypes = new CtClass[9];
+
+ booleanType =
+ new CtPrimitiveType("boolean", 'Z', "java.lang.Boolean",
+ "booleanValue", "()Z", Opcode.IRETURN,
+ Opcode.T_BOOLEAN, 1);
+ primitiveTypes[0] = booleanType;
+
+ charType = new CtPrimitiveType("char", 'C', "java.lang.Character",
+ "charValue", "()C", Opcode.IRETURN,
+ Opcode.T_CHAR, 1);
+ primitiveTypes[1] = charType;
+
+ byteType = new CtPrimitiveType("byte", 'B', "java.lang.Byte",
+ "byteValue", "()B", Opcode.IRETURN,
+ Opcode.T_BYTE, 1);
+ primitiveTypes[2] = byteType;
+
+ shortType = new CtPrimitiveType("short", 'S', "java.lang.Short",
+ "shortValue", "()S", Opcode.IRETURN,
+ Opcode.T_SHORT, 1);
+ primitiveTypes[3] = shortType;
+
+ intType = new CtPrimitiveType("int", 'I', "java.lang.Integer",
+ "intValue", "()I", Opcode.IRETURN,
+ Opcode.T_INT, 1);
+ primitiveTypes[4] = intType;
+
+ longType = new CtPrimitiveType("long", 'J', "java.lang.Long",
+ "longValue", "()J", Opcode.LRETURN,
+ Opcode.T_LONG, 2);
+ primitiveTypes[5] = longType;
+
+ floatType = new CtPrimitiveType("float", 'F', "java.lang.Float",
+ "floatValue", "()F", Opcode.FRETURN,
+ Opcode.T_FLOAT, 1);
+ primitiveTypes[6] = floatType;
+
+ doubleType = new CtPrimitiveType("double", 'D', "java.lang.Double",
+ "doubleValue", "()D", Opcode.DRETURN,
+ Opcode.T_DOUBLE, 2);
+ primitiveTypes[7] = doubleType;
+
+ voidType = new CtPrimitiveType("void", 'V', "java.lang.Void",
+ null, null, Opcode.RETURN, 0, 0);
+ primitiveTypes[8] = voidType;
+ }
+
+ protected CtClass(String name) {
+ qualifiedName = name;
+ }
+
+ /**
+ * Converts the object to a string.
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append("@");
+ buf.append(Integer.toHexString(hashCode()));
+ buf.append("[");
+ extendToString(buf);
+ buf.append("]");
+ return buf.toString();
+ }
+
+ /**
+ * Implemented in subclasses to add to the {@link #toString()} result.
+ * Subclasses should put a space before each token added to the buffer.
+ */
+ protected void extendToString(StringBuffer buffer) {
+ buffer.append(getName());
+ }
+
+ /**
+ * Returns a <code>ClassPool</code> for this class.
+ */
+ public ClassPool getClassPool() { return null; }
+
+ /**
+ * Returns a class file for this class.
+ *
+ * <p>This method is not available if <code>isFrozen()</code>
+ * is true.
+ */
+ public ClassFile getClassFile() {
+ checkModify();
+ return getClassFile2();
+ }
+
+ /**
+ * Undocumented method. Do not use; internal-use only.
+ */
+ public ClassFile getClassFile2() { return null; }
+
+ /**
+ * Undocumented method. Do not use; internal-use only.
+ */
+ public javassist.compiler.AccessorMaker getAccessorMaker() {
+ return null;
+ }
+
+ /**
+ * Returns the uniform resource locator (URL) of the class file.
+ */
+ public URL getURL() throws NotFoundException {
+ throw new NotFoundException(getName());
+ }
+
+ /**
+ * Returns true if the definition of the class has been modified.
+ */
+ public boolean isModified() { return false; }
+
+ /**
+ * Returns true if the class has been loaded or written out
+ * and thus it cannot be modified any more.
+ *
+ * @see #defrost()
+ */
+ public boolean isFrozen() { return true; }
+
+ void freeze() {}
+
+ void checkModify() throws RuntimeException {
+ if (isFrozen())
+ throw new RuntimeException(getName() + " class is frozen");
+
+ // isModified() must return true after this method is invoked.
+ }
+
+ /**
+ * Defrosts the class so that the class can be modified again.
+ *
+ * <p>To avoid changes that will be never reflected,
+ * the class is frozen to be unmodifiable if it is loaded or
+ * written out. This method should be called only in a case
+ * that the class will be reloaded or written out later again.
+ *
+ * <p>If <code>defrost()</code> will be called later, pruning
+ * must be disallowed in advance.
+ *
+ * @see #isFrozen()
+ * @see #stopPruning(boolean)
+ */
+ public void defrost() {
+ throw new RuntimeException("cannot defrost " + getName());
+ }
+
+ /**
+ * Disallows (or allows) pruning the data structure on memory
+ * when this <code>CtClass</code> object is converted into a class file.
+ * Pruning saves memory space since a <code>ClassPool</code> holds
+ * all instances of <code>CtClass</code>
+ * all the time of program execution.
+ * However, pruning discards the data representing part of the
+ * class definition, such as method bodies.
+ * Therefore, once it is pruned, <code>toBytecode()</code>,
+ * <code>writeFile()</code>, or <code>toClass()</code> cannot
+ * be called again.
+ *
+ * <p>Initially, pruning is allowed.
+ *
+ * @param stop disallow pruning if true. Otherwise, allow.
+ * @see #detach()
+ * @see #toBytecode()
+ * @see #toClass()
+ * @see #writeFile()
+ */
+ public void stopPruning(boolean stop) {}
+
+ /* Called by get() in ClassPool.
+ * CtClassType overrides this method.
+ */
+ void incGetCounter() {}
+
+ /**
+ * Returns <code>true</code> if this object represents a primitive
+ * Java type: boolean, byte, char, short, int, long, float, double,
+ * or void.
+ */
+ public boolean isPrimitive() { return false; }
+
+ /**
+ * Returns <code>true</code> if this object represents an array type.
+ */
+ public boolean isArray() {
+ return false;
+ }
+
+ /**
+ * If this object represents an array, this method returns the component
+ * type of the array. Otherwise, it returns <code>null</code>.
+ */
+ public CtClass getComponentType() throws NotFoundException {
+ return null;
+ }
+
+ /**
+ * Returns <code>true</code> if this class extends or implements
+ * <code>clazz</code>. It also returns <code>true</code> if
+ * this class is the same as <code>clazz</code>.
+ */
+ public boolean subtypeOf(CtClass clazz) throws NotFoundException {
+ return this == clazz || getName().equals(clazz.getName());
+ }
+
+ /**
+ * Obtains the fully-qualified name of the class.
+ */
+ public String getName() { return qualifiedName; }
+
+ /**
+ * Obtains the not-qualified class name.
+ */
+ public final String getSimpleName() {
+ String qname = qualifiedName;
+ int index = qname.lastIndexOf('.');
+ if (index < 0)
+ return qname;
+ else
+ return qname.substring(index + 1);
+ }
+
+ /**
+ * Obtains the package name. It may be <code>null</code>.
+ */
+ public final String getPackageName() {
+ String qname = qualifiedName;
+ int index = qname.lastIndexOf('.');
+ if (index < 0)
+ return null;
+ else
+ return qname.substring(0, index);
+ }
+
+ /**
+ * Sets the class name
+ *
+ * @param name fully-qualified name
+ */
+ public void setName(String name) {
+ checkModify();
+ if (name != null)
+ qualifiedName = name;
+ }
+
+ /**
+ * Substitutes <code>newName</code> for all occurrences of a class
+ * name <code>oldName</code> in the class file.
+ *
+ * @param oldName replaced class name
+ * @param newName substituted class name
+ */
+ public void replaceClassName(String oldName, String newName) {
+ checkModify();
+ }
+
+ /**
+ * Changes class names appearing in the class file according to the
+ * given <code>map</code>.
+ *
+ * <p>All the class names appearing in the class file are tested
+ * with <code>map</code> to determine whether each class name is
+ * replaced or not. Thus this method can be used for collecting
+ * all the class names in the class file. To do that, first define
+ * a subclass of <code>ClassMap</code> so that <code>get()</code>
+ * records all the given parameters. Then, make an instance of
+ * that subclass as an empty hash-table. Finally, pass that instance
+ * to this method. After this method finishes, that instance would
+ * contain all the class names appearing in the class file.
+ *
+ * @param map the hashtable associating replaced class names
+ * with substituted names.
+ */
+ public void replaceClassName(ClassMap map) {
+ checkModify();
+ }
+
+ /**
+ * Returns a collection of the names of all the classes
+ * referenced in this class.
+ * That collection includes the name of this class.
+ *
+ * <p>This method may return <code>null</code>.
+ */
+ public Collection getRefClasses() {
+ ClassFile cf = getClassFile2();
+ if (cf != null) {
+ ClassMap cm = new ClassMap() {
+ public void put(String oldname, String newname) {
+ put0(oldname, newname);
+ }
+
+ public Object get(Object jvmClassName) {
+ String n = toJavaName((String)jvmClassName);
+ put0(n, n);
+ return null;
+ }
+
+ public void fix(String name) {}
+ };
+ cf.renameClass(cm);
+ return cm.values();
+ }
+ else
+ return null;
+ }
+
+ /**
+ * Determines whether this object represents a class or an interface.
+ * It returns <code>true</code> if this object represents an interface.
+ */
+ public boolean isInterface() {
+ return false;
+ }
+
+ /**
+ * Returns the modifiers for this class, encoded in an integer.
+ * For decoding, use <code>javassist.Modifier</code>.
+ *
+ * @see Modifier
+ */
+ public int getModifiers() {
+ return 0;
+ }
+
+ /**
+ * Sets the modifiers.
+ *
+ * @param mod modifiers encoded by
+ * <code>javassist.Modifier</code>
+ * @see Modifier
+ */
+ public void setModifiers(int mod) {
+ checkModify();
+ }
+
+ /**
+ * Determines whether the class directly or indirectly extends
+ * the given class. If this class extends a class A and
+ * the class A extends a class B, then subclassof(B) returns true.
+ *
+ * <p>This method returns true if the given class is identical to
+ * the class represented by this object.
+ */
+ public boolean subclassOf(CtClass superclass) {
+ return false;
+ }
+
+ /**
+ * Obtains the class object representing the superclass of the
+ * class.
+ * It returns null if this object represents the
+ * <code>java.lang.Object</code> class and thus it does not have
+ * the super class.
+ *
+ * <p>If this object represents an interface, this method
+ * always returns the <code>java.lang.Object</code> class.
+ * To obtain the super interfaces
+ * extended by that interface, call <code>getInterfaces()</code>.
+ */
+ public CtClass getSuperclass() throws NotFoundException {
+ return null;
+ }
+
+ /**
+ * Changes a super class unless this object represents an interface.
+ * The new super class must be compatible with the old one.
+ *
+ * <p>If this object represents an interface, this method is equivalent
+ * to <code>addInterface()</code>; it appends <code>clazz</code> to
+ * the list of the super interfaces extended by that interface.
+ * Note that an interface can extend multiple super interfaces.
+ */
+ public void setSuperclass(CtClass clazz) throws CannotCompileException {
+ checkModify();
+ }
+
+ /**
+ * Obtains the class objects representing the interfaces implemented
+ * by the class or, if this object represents an interface, the interfaces
+ * extended by that interface.
+ */
+ public CtClass[] getInterfaces() throws NotFoundException {
+ return new CtClass[0];
+ }
+
+ /**
+ * Sets implemented interfaces. If this object represents an interface,
+ * this method sets the interfaces extended by that interface.
+ *
+ * @param list a list of the <code>CtClass</code> objects
+ * representing interfaces, or
+ * <code>null</code> if the class implements
+ * no interfaces.
+ */
+ public void setInterfaces(CtClass[] list) {
+ checkModify();
+ }
+
+ /**
+ * Adds an interface.
+ *
+ * @param anInterface the added interface.
+ */
+ public void addInterface(CtClass anInterface) {
+ checkModify();
+ }
+
+ /**
+ * If this class is a member class or interface of another class,
+ * then the class enclosing this class is returned.
+ *
+ * @return null if this class is a top-level class.
+ */
+ public CtClass getDeclaringClass() throws NotFoundException {
+ return null;
+ }
+
+ /**
+ * Makes a new nested class. Making a nested class modifies the
+ * data in this <code>CtClass</code>.
+ *
+ * <p>The current implementation only supports a static nested class.
+ * <code>isStatic</code> must be true.
+ *
+ * @param name the simple name of the nested class.
+ * @param isStatic true if the nested class is static.
+ */
+ public CtClass makeNestedClass(String name, boolean isStatic) {
+ throw new RuntimeException(getName() + " is not a class");
+ }
+
+ /**
+ * Returns an array containing <code>CtField</code> objects
+ * representing all the public fields of the class.
+ * That array includes public fields inherited from the
+ * superclasses.
+ */
+ public CtField[] getFields() { return new CtField[0]; }
+
+ /**
+ * Returns the field with the specified name. The returned field
+ * may be a private field declared in a super class or interface.
+ */
+ public CtField getField(String name) throws NotFoundException {
+ throw new NotFoundException(name);
+ }
+
+ /**
+ * @return null if the specified field is not found.
+ */
+ CtField getField2(String name) { return null; }
+
+ /**
+ * Gets all the fields declared in the class. The inherited fields
+ * are not included.
+ *
+ * <p>Note: the result does not include inherited fields.
+ */
+ public CtField[] getDeclaredFields() { return new CtField[0]; }
+
+ /**
+ * Retrieves the field with the specified name among the fields
+ * declared in the class.
+ *
+ * <p>Note: this method does not search the superclasses.
+ */
+ public CtField getDeclaredField(String name) throws NotFoundException {
+ throw new NotFoundException(name);
+ }
+
+ /**
+ * Gets all the constructors and methods declared in the class.
+ */
+ public CtBehavior[] getDeclaredBehaviors() {
+ return new CtBehavior[0];
+ }
+
+ /**
+ * Returns an array containing <code>CtConstructor</code> objects
+ * representing all the public constructors of the class.
+ */
+ public CtConstructor[] getConstructors() {
+ return new CtConstructor[0];
+ }
+
+ /**
+ * Returns the constructor with the given signature,
+ * which is represented by a character string
+ * called method descriptor.
+ * For details of the method descriptor, see the JVM specification
+ * or <code>javassist.bytecode.Descriptor</code>.
+ *
+ * @param desc method descriptor
+ * @see javassist.bytecode.Descriptor
+ */
+ public CtConstructor getConstructor(String desc)
+ throws NotFoundException
+ {
+ throw new NotFoundException("no such a constructor");
+ }
+
+ /**
+ * Gets all the constructors declared in the class.
+ *
+ * @see javassist.CtConstructor
+ */
+ public CtConstructor[] getDeclaredConstructors() {
+ return new CtConstructor[0];
+ }
+
+ /**
+ * Returns a constructor receiving the specified parameters.
+ *
+ * @param params parameter types.
+ */
+ public CtConstructor getDeclaredConstructor(CtClass[] params)
+ throws NotFoundException
+ {
+ String desc = Descriptor.ofConstructor(params);
+ return getConstructor(desc);
+ }
+
+ /**
+ * Gets the class initializer (static constructor)
+ * declared in the class.
+ * This method returns <code>null</code> if
+ * no class initializer is not declared.
+ *
+ * @see #makeClassInitializer()
+ * @see javassist.CtConstructor
+ */
+ public CtConstructor getClassInitializer() {
+ return null;
+ }
+
+ /**
+ * Returns an array containing <code>CtMethod</code> objects
+ * representing all the public methods of the class.
+ * That array includes public methods inherited from the
+ * superclasses.
+ */
+ public CtMethod[] getMethods() {
+ return new CtMethod[0];
+ }
+
+ /**
+ * Returns the method with the given name and signature.
+ * The returned method may be declared in a super class.
+ * The method signature is represented by a character string
+ * called method descriptor,
+ * which is defined in the JVM specification.
+ *
+ * @param name method name
+ * @param desc method descriptor
+ * @see javassist.bytecode.Descriptor
+ */
+ public CtMethod getMethod(String name, String desc)
+ throws NotFoundException
+ {
+ throw new NotFoundException(name);
+ }
+
+ /**
+ * Gets all methods declared in the class. The inherited methods
+ * are not included.
+ *
+ * @see javassist.CtMethod
+ */
+ public CtMethod[] getDeclaredMethods() {
+ return new CtMethod[0];
+ }
+
+ /**
+ * Retrieves the method with the specified name and parameter types
+ * among the methods declared in the class.
+ *
+ * <p>Note: this method does not search the superclasses.
+ *
+ * @param name method name
+ * @param params parameter types
+ * @see javassist.CtMethod
+ */
+ public CtMethod getDeclaredMethod(String name, CtClass[] params)
+ throws NotFoundException
+ {
+ throw new NotFoundException(name);
+ }
+
+ /**
+ * Retrieves the method with the specified name among the methods
+ * declared in the class. If there are multiple methods with
+ * the specified name, then this method returns one of them.
+ *
+ * <p>Note: this method does not search the superclasses.
+ *
+ * @see javassist.CtMethod
+ */
+ public CtMethod getDeclaredMethod(String name) throws NotFoundException {
+ throw new NotFoundException(name);
+ }
+
+ /**
+ * Makes an empty class initializer (static constructor).
+ * If the class already includes a class initializer,
+ * this method returns it.
+ *
+ * @see #getClassInitializer()
+ */
+ public CtConstructor makeClassInitializer()
+ throws CannotCompileException
+ {
+ throw new CannotCompileException("not a class");
+ }
+
+ /**
+ * Adds a constructor. To add a class initializer (static constructor),
+ * call <code>makeClassInitializer()</code>.
+ *
+ * @see #makeClassInitializer()
+ */
+ public void addConstructor(CtConstructor c)
+ throws CannotCompileException
+ {
+ checkModify();
+ }
+
+ /**
+ * Adds a method.
+ */
+ public void addMethod(CtMethod m) throws CannotCompileException {
+ checkModify();
+ }
+
+ /**
+ * Adds a field.
+ *
+ * <p>The <code>CtField</code> belonging to another
+ * <code>CtClass</code> cannot be directly added to this class.
+ * Only a field created for this class can be added.
+ *
+ * @see javassist.CtField#CtField(CtField,CtClass)
+ */
+ public void addField(CtField f) throws CannotCompileException {
+ addField(f, (CtField.Initializer)null);
+ }
+
+ /**
+ * Adds a field with an initial value.
+ *
+ * <p>The <code>CtField</code> belonging to another
+ * <code>CtClass</code> cannot be directly added to this class.
+ * Only a field created for this class can be added.
+ *
+ * <p>The initial value is given as an expression written in Java.
+ * Any regular Java expression can be used for specifying the initial
+ * value. The followings are examples.
+ *
+ * <ul><pre>
+ * cc.addField(f, "0") // the initial value is 0.
+ * cc.addField(f, "i + 1") // i + 1.
+ * cc.addField(f, "new Point()"); // a Point object.
+ * </pre></ul>
+ *
+ * <p>Here, the type of variable <code>cc</code> is <code>CtClass</code>.
+ * The type of <code>f</code> is <code>CtField</code>.
+ *
+ * @param init an expression for the initial value.
+ *
+ * @see javassist.CtField.Initializer#byExpr(String)
+ * @see javassist.CtField#CtField(CtField,CtClass)
+ */
+ public void addField(CtField f, String init)
+ throws CannotCompileException
+ {
+ checkModify();
+ }
+
+ /**
+ * Adds a field with an initial value.
+ *
+ * <p>The <code>CtField</code> belonging to another
+ * <code>CtClass</code> cannot be directly added to this class.
+ * Only a field created for this class can be added.
+ *
+ * <p>For example,
+ *
+ * <ul><pre>
+ * CtClass cc = ...;
+ * addField(new CtField(CtClass.intType, "i", cc),
+ * CtField.Initializer.constant(1));
+ * </pre></ul>
+ *
+ * <p>This code adds an <code>int</code> field named "i". The
+ * initial value of this field is 1.
+ *
+ * @param init specifies the initial value of the field.
+ *
+ * @see javassist.CtField#CtField(CtField,CtClass)
+ */
+ public void addField(CtField f, CtField.Initializer init)
+ throws CannotCompileException
+ {
+ checkModify();
+ }
+
+ /**
+ * Obtains an attribute with the given name.
+ * If that attribute is not found in the class file, this
+ * method returns null.
+ *
+ * <p>This is a convenient method mainly for obtaining
+ * a user-defined attribute. For dealing with attributes, see the
+ * <code>javassist.bytecode</code> package. For example, the following
+ * expression returns all the attributes of a class file.
+ *
+ * <ul><pre>
+ * getClassFile().getAttributes()
+ * </pre></ul>
+ *
+ * @param name attribute name
+ * @see javassist.bytecode.AttributeInfo
+ */
+ public byte[] getAttribute(String name) {
+ return null;
+ }
+
+ /**
+ * Adds a named attribute.
+ * An arbitrary data (smaller than 64Kb) can be saved in the class
+ * file. Some attribute name are reserved by the JVM.
+ * The attributes with the non-reserved names are ignored when a
+ * class file is loaded into the JVM.
+ * If there is already an attribute with
+ * the same name, this method substitutes the new one for it.
+ *
+ * <p>This is a convenient method mainly for adding
+ * a user-defined attribute. For dealing with attributes, see the
+ * <code>javassist.bytecode</code> package. For example, the following
+ * expression adds an attribute of a class file.
+ *
+ * <ul><pre>
+ * getClassFile().addAttribute(info)
+ * </pre></ul>
+ *
+ * @param name attribute name
+ * @param data attribute value
+ * @see javassist.bytecode.AttributeInfo
+ */
+ public void setAttribute(String name, byte[] data) {
+ checkModify();
+ }
+
+ /**
+ * Applies the given converter to all methods and constructors
+ * declared in the class. This method calls <code>instrument()</code>
+ * on every <code>CtMethod</code> and <code>CtConstructor</code> object
+ * in the class.
+ *
+ * @param converter specifies how to modify.
+ */
+ public void instrument(CodeConverter converter)
+ throws CannotCompileException
+ {
+ checkModify();
+ }
+
+ /**
+ * Modifies the bodies of all methods and constructors
+ * declared in the class. This method calls <code>instrument()</code>
+ * on every <code>CtMethod</code> and <code>CtConstructor</code> object
+ * in the class.
+ *
+ * @param editor specifies how to modify.
+ */
+ public void instrument(ExprEditor editor)
+ throws CannotCompileException
+ {
+ checkModify();
+ }
+
+ /**
+ * Converts this class to a <code>java.lang.Class</code> object.
+ * Once this method is called, further modifications are not
+ * allowed any more.
+ * To load the class, this method uses the context class loader
+ * of the current thread. If the program is running on some application
+ * server, the context class loader might be inappropriate to load the
+ * class.
+ *
+ * <p>This method is provided for convenience. If you need more
+ * complex functionality, you should write your own class loader.
+ *
+ * <p>Note: this method calls <code>toClass()</code>
+ * in <code>ClassPool</code>.
+ *
+ * @see #toClass(java.lang.ClassLoader)
+ * @see ClassPool#toClass(java.lang.ClassLoader)
+ */
+ public Class toClass() throws CannotCompileException {
+ return getClassPool().toClass(this);
+ }
+
+ /**
+ * Converts this class to a <code>java.lang.Class</code> object.
+ * Once this method is called, further modifications are not allowed
+ * any more.
+ *
+ * <p>The class file represented by this <code>CtClass</code> is
+ * loaded by the given class loader to construct a
+ * <code>java.lang.Class</code> object. Since a private method
+ * on the class loader is invoked through the reflection API,
+ * the caller must have permissions to do that.
+ *
+ * <p>This method is provided for convenience. If you need more
+ * complex functionality, you should write your own class loader.
+ *
+ * <p>Note: this method calls <code>toClass()</code>
+ * in <code>ClassPool</code>.
+ *
+ * @param loader the class loader used to load this class.
+ * @see ClassPool#toClass(CtClass,java.lang.ClassLoader)
+ */
+ public Class toClass(ClassLoader loader)
+ throws CannotCompileException
+ {
+ return getClassPool().toClass(this, loader);
+ }
+
+ /**
+ * Removes this <code>CtClass</code> object from the
+ * <code>ClassPool</code>.
+ * After this method is called, any method cannot be called on the
+ * removed <code>CtClass</code> object.
+ *
+ * <p>If <code>get()</code> in <code>ClassPool</code> is called
+ * with the name of the removed method,
+ * the <code>ClassPool</code> will read the class file again
+ * and constructs another <code>CtClass</code> object representing
+ * the same class.
+ */
+ public void detach() {
+ ClassPool cp = getClassPool();
+ CtClass obj = cp.removeCached(getName());
+ if (obj != this)
+ cp.cacheCtClass(getName(), obj);
+ }
+
+ /**
+ * Converts this class to a class file.
+ * Once this method is called, further modifications are not
+ * possible any more.
+ *
+ * @return the contents of the class file.
+ */
+ public byte[] toBytecode() throws IOException, CannotCompileException {
+ ByteArrayOutputStream barray = new ByteArrayOutputStream();
+ DataOutputStream out = new DataOutputStream(barray);
+ try {
+ toBytecode(out);
+ }
+ finally {
+ out.close();
+ }
+
+ return barray.toByteArray();
+ }
+
+ /**
+ * Writes a class file represented by this <code>CtClass</code>
+ * object in the current directory.
+ * Once this method is called, further modifications are not
+ * possible any more.
+ */
+ public void writeFile()
+ throws NotFoundException, IOException, CannotCompileException
+ {
+ writeFile(".");
+ }
+
+ /**
+ * Writes a class file represented by this <code>CtClass</code>
+ * object on a local disk.
+ * Once this method is called, further modifications are not
+ * possible any more.
+ *
+ * @param directoryName it must end without a directory separator.
+ */
+ public void writeFile(String directoryName)
+ throws NotFoundException, CannotCompileException, IOException
+ {
+ String classname = getName();
+ String filename = directoryName + File.separatorChar
+ + classname.replace('.', File.separatorChar) + ".class";
+ int pos = filename.lastIndexOf(File.separatorChar);
+ if (pos > 0) {
+ String dir = filename.substring(0, pos);
+ if (!dir.equals("."))
+ new File(dir).mkdirs();
+ }
+
+ DataOutputStream out
+ = new DataOutputStream(new BufferedOutputStream(
+ new DelayedFileOutputStream(filename)));
+ try {
+ toBytecode(out);
+ }
+ finally {
+ out.close();
+ }
+ }
+
+ static class DelayedFileOutputStream extends OutputStream {
+ private FileOutputStream file;
+ private String filename;
+
+ DelayedFileOutputStream(String name) {
+ file = null;
+ filename = name;
+ }
+
+ private void init() throws IOException {
+ if (file == null)
+ file = new FileOutputStream(filename);
+ }
+
+ public void write(int b) throws IOException {
+ init();
+ file.write(b);
+ }
+
+ public void write(byte[] b) throws IOException {
+ init();
+ file.write(b);
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ init();
+ file.write(b, off, len);
+
+ }
+
+ public void flush() throws IOException {
+ init();
+ file.flush();
+ }
+
+ public void close() throws IOException {
+ init();
+ file.close();
+ }
+ }
+
+ /**
+ * Converts this class to a class file.
+ * Once this method is called, further modifications are not
+ * possible any more.
+ *
+ * <p>This method dose not close the output stream in the end.
+ *
+ * @param out the output stream that a class file is written to.
+ */
+ public void toBytecode(DataOutputStream out)
+ throws CannotCompileException, IOException
+ {
+ throw new CannotCompileException("not a class");
+ }
}
/**
* Class types.
*/
-class CtClassType extends CtClass
-{
- ClassPool classPool;
- boolean wasChanged;
- private boolean wasFrozen;
- boolean wasPruned;
- ClassFile classfile;
-
- private CtField fieldsCache;
- private CtConstructor constructorsCache;
- private CtConstructor classInitializerCache;
- private CtMethod methodsCache;
-
- private AccessorMaker accessors;
-
- private FieldInitLink fieldInitializers;
- private Hashtable hiddenMethods; // must be synchronous
- private int uniqueNumberSeed;
-
- private boolean doPruning = false;
- int getCounter;
- private static int readCounter = 0;
- private static final int READ_THRESHOLD = 100; // see getClassFile2()
-
- CtClassType(String name, ClassPool cp)
- {
- super(name);
- classPool = cp;
- wasChanged = wasFrozen = wasPruned = false;
- classfile = null;
- accessors = null;
- fieldInitializers = null;
- hiddenMethods = null;
- uniqueNumberSeed = 0;
- eraseCache();
- getCounter = 0;
- }
-
- CtClassType(InputStream ins, ClassPool cp) throws IOException
- {
- this((String) null, cp);
- classfile = new ClassFile(new DataInputStream(ins));
- qualifiedName = classfile.getName();
- }
-
- protected void extendToString(StringBuffer buffer)
- {
- if (wasChanged)
- buffer.append("changed ");
-
- if (wasFrozen)
- buffer.append("frozen ");
-
- if (wasPruned)
- buffer.append("pruned ");
-
- buffer.append(Modifier.toString(getModifiers()));
- buffer.append(" class ");
- buffer.append(getName());
-
- try
- {
- CtClass ext = getSuperclass();
- if (ext != null)
- {
- String name = ext.getName();
- if (!name.equals("java.lang.Object"))
- buffer.append(" extends " + ext.getName());
- }
- }
- catch (NotFoundException e)
- {
- buffer.append(" extends ??");
- }
-
- try
- {
- CtClass[] intf = getInterfaces();
- if (intf.length > 0)
- buffer.append(" implements ");
-
- for (int i = 0; i < intf.length; ++i)
- {
- buffer.append(intf[i].getName());
- buffer.append(", ");
- }
- }
- catch (NotFoundException e)
- {
- buffer.append(" extends ??");
- }
-
- CtField field = getFieldsCache();
- buffer.append(" fields=");
- while (field != null)
- {
- buffer.append(field);
- buffer.append(", ");
- field = field.next;
- }
-
- CtConstructor c = getConstructorsCache();
- buffer.append(" constructors=");
- while (c != null)
- {
- buffer.append(c);
- buffer.append(", ");
- c = c.next;
- }
-
- CtMethod m = getMethodsCache();
- buffer.append(" methods=");
- while (m != null)
- {
- buffer.append(m);
- buffer.append(", ");
- m = m.next;
- }
- }
-
- protected void eraseCache()
- {
- fieldsCache = null;
- constructorsCache = null;
- classInitializerCache = null;
- methodsCache = null;
- }
-
- public AccessorMaker getAccessorMaker()
- {
- if (accessors == null)
- accessors = new AccessorMaker(this);
-
- return accessors;
- }
-
- public ClassFile getClassFile2()
- {
- if (classfile != null)
- return classfile;
-
- if (readCounter++ > READ_THRESHOLD)
- {
- doCompaction();
- readCounter = 0;
- }
-
- InputStream fin = null;
- try
- {
- fin = classPool.openClassfile(getName());
- if (fin == null)
- throw new NotFoundException(getName());
-
- fin = new BufferedInputStream(fin);
- classfile = new ClassFile(new DataInputStream(fin));
- return classfile;
- }
- catch (NotFoundException e)
- {
- throw new RuntimeException(e.toString());
- }
- catch (IOException e)
- {
- throw new RuntimeException(e.toString());
- }
- finally
- {
- if (fin != null)
- try
- {
- fin.close();
+class CtClassType extends CtClass {
+ ClassPool classPool;
+ boolean wasChanged;
+ private boolean wasFrozen;
+ boolean wasPruned;
+ ClassFile classfile;
+
+ private CtField fieldsCache;
+ private CtConstructor constructorsCache;
+ private CtConstructor classInitializerCache;
+ private CtMethod methodsCache;
+
+ private AccessorMaker accessors;
+
+ private FieldInitLink fieldInitializers;
+ private Hashtable hiddenMethods; // must be synchronous
+ private int uniqueNumberSeed;
+
+ private boolean doPruning = false;
+ int getCounter;
+ private static int readCounter = 0;
+ private static final int READ_THRESHOLD = 100; // see getClassFile2()
+
+ CtClassType(String name, ClassPool cp) {
+ super(name);
+ classPool = cp;
+ wasChanged = wasFrozen = wasPruned = false;
+ classfile = null;
+ accessors = null;
+ fieldInitializers = null;
+ hiddenMethods = null;
+ uniqueNumberSeed = 0;
+ eraseCache();
+ getCounter = 0;
+ }
+
+ CtClassType(InputStream ins, ClassPool cp) throws IOException {
+ this((String)null, cp);
+ classfile = new ClassFile(new DataInputStream(ins));
+ qualifiedName = classfile.getName();
+ }
+
+ protected void extendToString(StringBuffer buffer) {
+ if (wasChanged)
+ buffer.append("changed ");
+
+ if (wasFrozen)
+ buffer.append("frozen ");
+
+ if (wasPruned)
+ buffer.append("pruned ");
+
+ buffer.append(Modifier.toString(getModifiers()));
+ buffer.append(" class ");
+ buffer.append(getName());
+
+ try {
+ CtClass ext = getSuperclass();
+ if (ext != null) {
+ String name = ext.getName();
+ if (!name.equals("java.lang.Object"))
+ buffer.append(" extends " + ext.getName());
}
- catch (IOException e)
- {
+ }
+ catch (NotFoundException e) {
+ buffer.append(" extends ??");
+ }
+
+ try {
+ CtClass[] intf = getInterfaces();
+ if (intf.length > 0)
+ buffer.append(" implements ");
+
+ for (int i = 0; i < intf.length; ++i) {
+ buffer.append(intf[i].getName());
+ buffer.append(", ");
}
- }
- }
-
- /* Inherited from CtClass. Called by get() in ClassPool.
- *
- * @see javassist.CtClass#incGetCounter()
- */
- void incGetCounter()
- {
- ++getCounter;
- }
-
- private void doCompaction()
- {
- Enumeration e = classPool.classes.elements();
- while (e.hasMoreElements())
- {
- Object obj = e.nextElement();
- if (obj instanceof CtClassType)
- {
- CtClassType cct = (CtClassType) obj;
- if (cct.getCounter < 2 && !cct.isModified())
- {
- cct.eraseCache();
- cct.classfile = null;
+ }
+ catch (NotFoundException e) {
+ buffer.append(" extends ??");
+ }
+
+ CtField field = getFieldsCache();
+ buffer.append(" fields=");
+ while (field != null) {
+ buffer.append(field);
+ buffer.append(", ");
+ field = field.next;
+ }
+
+ CtConstructor c = getConstructorsCache();
+ buffer.append(" constructors=");
+ while (c != null) {
+ buffer.append(c);
+ buffer.append(", ");
+ c = c.next;
+ }
+
+ CtMethod m = getMethodsCache();
+ buffer.append(" methods=");
+ while (m != null) {
+ buffer.append(m);
+ buffer.append(", ");
+ m = m.next;
+ }
+ }
+
+ protected void eraseCache() {
+ fieldsCache = null;
+ constructorsCache = null;
+ classInitializerCache = null;
+ methodsCache = null;
+ }
+
+ public AccessorMaker getAccessorMaker() {
+ if (accessors == null)
+ accessors = new AccessorMaker(this);
+
+ return accessors;
+ }
+
+ public ClassFile getClassFile2() {
+ if (classfile != null)
+ return classfile;
+
+ if (readCounter++ > READ_THRESHOLD) {
+ doCompaction();
+ readCounter = 0;
+ }
+
+ InputStream fin = null;
+ try {
+ fin = classPool.openClassfile(getName());
+ if (fin == null)
+ throw new NotFoundException(getName());
+
+ fin = new BufferedInputStream(fin);
+ classfile = new ClassFile(new DataInputStream(fin));
+ return classfile;
+ }
+ catch (NotFoundException e) {
+ throw new RuntimeException(e.toString());
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e.toString());
+ }
+ finally {
+ if (fin != null)
+ try {
+ fin.close();
+ }
+ catch (IOException e) {}
+ }
+ }
+
+ /* Inherited from CtClass. Called by get() in ClassPool.
+ *
+ * @see javassist.CtClass#incGetCounter()
+ */
+ void incGetCounter() { ++getCounter; }
+
+ private void doCompaction() {
+ Enumeration e = classPool.classes.elements();
+ while (e.hasMoreElements()) {
+ Object obj = e.nextElement();
+ if (obj instanceof CtClassType) {
+ CtClassType cct = (CtClassType)obj;
+ if (cct.getCounter < 2 && !cct.isModified()) {
+ cct.eraseCache();
+ cct.classfile = null;
+ }
+
+ cct.getCounter = 0;
}
+ }
+ }
+
+ public ClassPool getClassPool() { return classPool; }
+
+ void setClassPool(ClassPool cp) { classPool = cp; }
+
+ public URL getURL() throws NotFoundException {
+ URL url = classPool.find(getName());
+ if (url == null)
+ throw new NotFoundException(getName());
+ else
+ return url;
+ }
+
+ public boolean isModified() { return wasChanged; }
- cct.getCounter = 0;
- }
- }
- }
-
- public ClassPool getClassPool()
- {
- return classPool;
- }
-
- void setClassPool(ClassPool cp)
- {
- classPool = cp;
- }
-
- public URL getURL() throws NotFoundException
- {
- URL url = classPool.find(getName());
- if (url == null)
- throw new NotFoundException(getName());
- else
- return url;
- }
-
- public boolean isModified()
- {
- return wasChanged;
- }
-
- public boolean isFrozen()
- {
- return wasFrozen;
- }
-
- void freeze()
- {
- wasFrozen = true;
- }
-
- void checkModify() throws RuntimeException
- {
- super.checkModify();
- wasChanged = true;
- }
-
- public void defrost()
- {
- checkPruned("defrost");
- wasFrozen = false;
- }
-
- public boolean subtypeOf(CtClass clazz) throws NotFoundException
- {
- int i;
- String cname = clazz.getName();
- if (this == clazz || getName().equals(cname))
- return true;
-
- ClassFile file = getClassFile2();
- String supername = file.getSuperclass();
- if (supername != null && supername.equals(cname))
- return true;
-
- String[] ifs = file.getInterfaces();
- int num = ifs.length;
- for (i = 0; i < num; ++i)
- if (ifs[i].equals(cname))
+ public boolean isFrozen() { return wasFrozen; }
+
+ void freeze() { wasFrozen = true; }
+
+ void checkModify() throws RuntimeException {
+ super.checkModify();
+ wasChanged = true;
+ }
+
+ public void defrost() {
+ checkPruned("defrost");
+ wasFrozen = false;
+ }
+
+ public boolean subtypeOf(CtClass clazz) throws NotFoundException {
+ int i;
+ String cname = clazz.getName();
+ if (this == clazz || getName().equals(cname))
+ return true;
+
+ ClassFile file = getClassFile2();
+ String supername = file.getSuperclass();
+ if (supername != null && supername.equals(cname))
return true;
- if (supername != null && classPool.get(supername).subtypeOf(clazz))
- return true;
+ String[] ifs = file.getInterfaces();
+ int num = ifs.length;
+ for (i = 0; i < num; ++i)
+ if (ifs[i].equals(cname))
+ return true;
- for (i = 0; i < num; ++i)
- if (classPool.get(ifs[i]).subtypeOf(clazz))
+ if (supername != null && classPool.get(supername).subtypeOf(clazz))
return true;
- return false;
- }
-
- public void setName(String name) throws RuntimeException
- {
- String oldname = getName();
- if (name.equals(oldname))
- return;
-
- // check this in advance although classNameChanged() below does.
- classPool.checkNotFrozen(name);
- ClassFile cf = getClassFile2();
- super.setName(name);
- cf.setName(name);
- eraseCache();
- classPool.classNameChanged(oldname, this);
- }
-
- public void replaceClassName(ClassMap classnames)
- throws RuntimeException
- {
- String oldClassName = getName();
- String newClassName
- = (String) classnames.get(Descriptor.toJvmName(oldClassName));
- if (newClassName != null)
- {
- newClassName = Descriptor.toJavaName(newClassName);
- // check this in advance although classNameChanged() below does.
- classPool.checkNotFrozen(newClassName);
- }
-
- super.replaceClassName(classnames);
- ClassFile cf = getClassFile2();
- cf.renameClass(classnames);
- eraseCache();
-
- if (newClassName != null)
- {
- super.setName(newClassName);
- classPool.classNameChanged(oldClassName, this);
- }
- }
-
- public void replaceClassName(String oldname, String newname)
- throws RuntimeException
- {
- String thisname = getName();
- if (thisname.equals(oldname))
- setName(newname);
- else
- {
- super.replaceClassName(oldname, newname);
- getClassFile2().renameClass(oldname, newname);
- eraseCache();
- }
- }
-
- public boolean isInterface()
- {
- return Modifier.isInterface(getModifiers());
- }
-
- public int getModifiers()
- {
- int acc = getClassFile2().getAccessFlags();
- acc = AccessFlag.clear(acc, AccessFlag.SUPER);
- return AccessFlag.toModifier(acc);
- }
-
- public void setModifiers(int mod)
- {
- checkModify();
- int acc = AccessFlag.of(mod) | AccessFlag.SUPER;
- getClassFile2().setAccessFlags(acc);
- }
-
- public boolean subclassOf(CtClass superclass)
- {
- if (superclass == null)
- return false;
-
- String superName = superclass.getName();
- CtClass curr = this;
- try
- {
- while (curr != null)
- {
- if (curr.getName().equals(superName))
- return true;
-
- curr = curr.getSuperclass();
- }
- }
- catch (Exception ignored)
- {
- }
- return false;
- }
-
- public CtClass getSuperclass() throws NotFoundException
- {
- String supername = getClassFile2().getSuperclass();
- if (supername == null)
- return null;
- else
- return classPool.get(supername);
- }
-
- public void setSuperclass(CtClass clazz) throws CannotCompileException
- {
- checkModify();
- if (isInterface())
- addInterface(clazz);
- else
- getClassFile2().setSuperclass(clazz.getName());
- }
-
- public CtClass[] getInterfaces() throws NotFoundException
- {
- String[] ifs = getClassFile2().getInterfaces();
- int num = ifs.length;
- CtClass[] ifc = new CtClass[num];
- for (int i = 0; i < num; ++i)
- ifc[i] = classPool.get(ifs[i]);
-
- return ifc;
- }
-
- public void setInterfaces(CtClass[] list)
- {
- checkModify();
- String[] ifs;
- if (list == null)
- ifs = new String[0];
- else
- {
- int num = list.length;
- ifs = new String[num];
- for (int i = 0; i < num; ++i)
- ifs[i] = list[i].getName();
- }
-
- getClassFile2().setInterfaces(ifs);
- }
-
- public void addInterface(CtClass anInterface)
- {
- checkModify();
- if (anInterface != null)
- getClassFile2().addInterface(anInterface.getName());
- }
-
- public CtClass getDeclaringClass() throws NotFoundException
- {
- ClassFile cf = getClassFile2();
- InnerClassesAttribute ica = (InnerClassesAttribute) cf.getAttribute(InnerClassesAttribute.tag);
- if (ica == null)
- return null;
-
- String name = getName();
- int n = ica.tableLength();
- for (int i = 0; i < n; ++i)
- if (name.equals(ica.innerClass(i)))
- return classPool.get(ica.outerClass(i));
-
- return null;
- }
-
- public CtClass makeNestedClass(String name, boolean isStatic)
- {
- if (!isStatic)
- throw new RuntimeException("sorry, only nested static class is supported");
-
- checkModify();
- CtClass c = classPool.makeNestedClass(getName() + "$" + name);
- ClassFile cf = getClassFile2();
- InnerClassesAttribute ica = (InnerClassesAttribute) cf.getAttribute(InnerClassesAttribute.tag);
- if (ica == null)
- {
- ica = new InnerClassesAttribute(cf.getConstPool());
- cf.addAttribute(ica);
- }
-
- ica.append(c.getName(), this.getName(), name, AccessFlag.STATIC);
- ClassFile cf2 = c.getClassFile2();
- cf2.addAttribute(ica.copy(cf2.getConstPool(), null));
- return c;
- }
-
- public CtField[] getFields()
- {
- ArrayList alist = new ArrayList();
- getFields(alist, this);
- return (CtField[]) alist.toArray(new CtField[alist.size()]);
- }
-
- private static void getFields(ArrayList alist, CtClass cc)
- {
- int i, num;
- if (cc == null)
- return;
-
- try
- {
- getFields(alist, cc.getSuperclass());
- }
- catch (NotFoundException e)
- {
- }
-
- try
- {
- CtClass[] ifs = cc.getInterfaces();
- num = ifs.length;
- for (i = 0; i < num; ++i)
- getFields(alist, ifs[i]);
- }
- catch (NotFoundException e)
- {
- }
-
- CtField cf = ((CtClassType) cc).getFieldsCache();
- while (cf != null)
- {
- if (Modifier.isPublic(cf.getModifiers()))
- alist.add(cf);
-
- cf = cf.next;
- }
- }
-
- public CtField getField(String name) throws NotFoundException
- {
- CtField f = getField2(name);
- if (f == null)
- throw new NotFoundException("field: " + name + " in " + getName());
- else
- return f;
- }
-
- CtField getField2(String name)
- {
- CtField df = getDeclaredField2(name);
- if (df != null)
- return df;
-
- try
- {
- CtClass[] ifs = getInterfaces();
- int num = ifs.length;
- for (int i = 0; i < num; ++i)
- {
- CtField f = ifs[i].getField2(name);
- if (f != null)
- return f;
- }
-
- CtClass s = getSuperclass();
- if (s != null)
- return s.getField2(name);
- }
- catch (NotFoundException e)
- {
- }
- return null;
- }
-
- public CtField[] getDeclaredFields()
- {
- CtField cf = getFieldsCache();
- int num = CtField.count(cf);
- CtField[] cfs = new CtField[num];
- int i = 0;
- while (cf != null)
- {
- cfs[i++] = cf;
- cf = cf.next;
- }
-
- return cfs;
- }
-
- protected CtField getFieldsCache()
- {
- if (fieldsCache == null)
- {
- List list = getClassFile2().getFields();
- int n = list.size();
- for (int i = 0; i < n; ++i)
- {
- FieldInfo finfo = (FieldInfo) list.get(i);
- fieldsCache = CtField.append(fieldsCache,
- new CtField(finfo, this));
- }
- }
-
- return fieldsCache;
- }
-
- public CtField getDeclaredField(String name) throws NotFoundException
- {
- CtField f = getDeclaredField2(name);
- if (f == null)
- throw new NotFoundException("field: " + name + " in " + getName());
- else
- return f;
- }
-
- private CtField getDeclaredField2(String name)
- {
- CtField cf = getFieldsCache();
- while (cf != null)
- {
- if (cf.getName().equals(name))
- return cf;
-
- cf = cf.next;
- }
-
- return null;
- }
-
- public CtBehavior[] getDeclaredBehaviors()
- {
- CtConstructor cc = getConstructorsCache();
- CtMethod cm = getMethodsCache();
- int num = CtMethod.count(cm) + CtConstructor.count(cc);
- CtBehavior[] cb = new CtBehavior[num];
- int i = 0;
- while (cc != null)
- {
- cb[i++] = cc;
- cc = cc.next;
- }
-
- while (cm != null)
- {
- cb[i++] = cm;
- cm = cm.next;
- }
-
- return cb;
- }
-
- public CtConstructor[] getConstructors()
- {
- CtConstructor[] cons = getDeclaredConstructors();
- if (cons.length == 0)
- return cons;
-
- int n = 0;
- int i = cons.length;
- while (--i >= 0)
- if (Modifier.isPublic(cons[i].getModifiers()))
- ++n;
-
- CtConstructor[] result = new CtConstructor[n];
- n = 0;
- i = cons.length;
- while (--i >= 0)
- {
- CtConstructor c = cons[i];
- if (Modifier.isPublic(c.getModifiers()))
- result[n++] = c;
- }
-
- return result;
- }
-
- public CtConstructor getConstructor(String desc)
- throws NotFoundException
- {
- CtConstructor cc = getConstructorsCache();
- while (cc != null)
- {
- if (cc.getMethodInfo2().getDescriptor().equals(desc))
- return cc;
-
- cc = cc.next;
- }
-
- return super.getConstructor(desc);
- }
-
- public CtConstructor[] getDeclaredConstructors()
- {
- CtConstructor cc = getConstructorsCache();
- int num = CtConstructor.count(cc);
- CtConstructor[] ccs = new CtConstructor[num];
- int i = 0;
- while (cc != null)
- {
- ccs[i++] = cc;
- cc = cc.next;
- }
-
- return ccs;
- }
-
- protected CtConstructor getConstructorsCache()
- {
- if (constructorsCache == null)
- {
- List list = getClassFile2().getMethods();
- int n = list.size();
- for (int i = 0; i < n; ++i)
- {
- MethodInfo minfo = (MethodInfo) list.get(i);
- if (minfo.isConstructor())
- constructorsCache
- = CtConstructor.append(constructorsCache,
- new CtConstructor(minfo, this));
- }
- }
-
- return constructorsCache;
- }
-
- public CtConstructor getClassInitializer()
- {
- if (classInitializerCache == null)
- {
- MethodInfo minfo = getClassFile2().getStaticInitializer();
- if (minfo != null)
- classInitializerCache = new CtConstructor(minfo, this);
- }
-
- return classInitializerCache;
- }
-
- public CtMethod[] getMethods()
- {
- HashMap h = new HashMap();
- getMethods0(h, this);
- return (CtMethod[]) h.values().toArray(new CtMethod[0]);
- }
-
- private static void getMethods0(HashMap h, CtClass cc)
- {
- try
- {
- CtClass[] ifs = cc.getInterfaces();
- int size = ifs.length;
- for (int i = 0; i < size; ++i)
- getMethods0(h, ifs[i]);
- }
- catch (NotFoundException e)
- {
- }
-
- try
- {
- CtClass s = cc.getSuperclass();
- if (s != null)
- getMethods0(h, s);
- }
- catch (NotFoundException e)
- {
- }
-
- if (cc instanceof CtClassType)
- {
- CtMethod cm = ((CtClassType) cc).getMethodsCache();
- while (cm != null)
- {
- if (Modifier.isPublic(cm.getModifiers()))
- h.put(cm.getStringRep(), cm);
+ for (i = 0; i < num; ++i)
+ if (classPool.get(ifs[i]).subtypeOf(clazz))
+ return true;
+
+ return false;
+ }
+
+ public void setName(String name) throws RuntimeException {
+ String oldname = getName();
+ if (name.equals(oldname))
+ return;
+
+ // check this in advance although classNameChanged() below does.
+ classPool.checkNotFrozen(name);
+ ClassFile cf = getClassFile2();
+ super.setName(name);
+ cf.setName(name);
+ eraseCache();
+ classPool.classNameChanged(oldname, this);
+ }
+
+ public void replaceClassName(ClassMap classnames)
+ throws RuntimeException
+ {
+ String oldClassName = getName();
+ String newClassName
+ = (String)classnames.get(Descriptor.toJvmName(oldClassName));
+ if (newClassName != null) {
+ newClassName = Descriptor.toJavaName(newClassName);
+ // check this in advance although classNameChanged() below does.
+ classPool.checkNotFrozen(newClassName);
+ }
+
+ super.replaceClassName(classnames);
+ ClassFile cf = getClassFile2();
+ cf.renameClass(classnames);
+ eraseCache();
+
+ if (newClassName != null) {
+ super.setName(newClassName);
+ classPool.classNameChanged(oldClassName, this);
+ }
+ }
+
+ public void replaceClassName(String oldname, String newname)
+ throws RuntimeException
+ {
+ String thisname = getName();
+ if (thisname.equals(oldname))
+ setName(newname);
+ else {
+ super.replaceClassName(oldname, newname);
+ getClassFile2().renameClass(oldname, newname);
+ eraseCache();
+ }
+ }
+
+ public boolean isInterface() {
+ return Modifier.isInterface(getModifiers());
+ }
+
+ public int getModifiers() {
+ int acc = getClassFile2().getAccessFlags();
+ acc = AccessFlag.clear(acc, AccessFlag.SUPER);
+ return AccessFlag.toModifier(acc);
+ }
+
+ public void setModifiers(int mod) {
+ checkModify();
+ int acc = AccessFlag.of(mod) | AccessFlag.SUPER;
+ getClassFile2().setAccessFlags(acc);
+ }
+
+ public boolean subclassOf(CtClass superclass) {
+ if (superclass == null)
+ return false;
+
+ String superName = superclass.getName();
+ CtClass curr = this;
+ try {
+ while (curr != null) {
+ if (curr.getName().equals(superName))
+ return true;
+
+ curr = curr.getSuperclass();
+ }
+ }
+ catch (Exception ignored) {}
+ return false;
+ }
+
+ public CtClass getSuperclass() throws NotFoundException {
+ String supername = getClassFile2().getSuperclass();
+ if (supername == null)
+ return null;
+ else
+ return classPool.get(supername);
+ }
+
+ public void setSuperclass(CtClass clazz) throws CannotCompileException {
+ checkModify();
+ if (isInterface())
+ addInterface(clazz);
+ else
+ getClassFile2().setSuperclass(clazz.getName());
+ }
+
+ public CtClass[] getInterfaces() throws NotFoundException {
+ String[] ifs = getClassFile2().getInterfaces();
+ int num = ifs.length;
+ CtClass[] ifc = new CtClass[num];
+ for (int i = 0; i < num; ++i)
+ ifc[i] = classPool.get(ifs[i]);
+
+ return ifc;
+ }
+
+ public void setInterfaces(CtClass[] list) {
+ checkModify();
+ String[] ifs;
+ if (list == null)
+ ifs = new String[0];
+ else {
+ int num = list.length;
+ ifs = new String[num];
+ for (int i = 0; i < num; ++i)
+ ifs[i] = list[i].getName();
+ }
+
+ getClassFile2().setInterfaces(ifs);
+ }
+
+ public void addInterface(CtClass anInterface) {
+ checkModify();
+ if (anInterface != null)
+ getClassFile2().addInterface(anInterface.getName());
+ }
+
+ public CtClass getDeclaringClass() throws NotFoundException {
+ ClassFile cf = getClassFile2();
+ InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute(
+ InnerClassesAttribute.tag);
+ if (ica == null)
+ return null;
+
+ String name = getName();
+ int n = ica.tableLength();
+ for (int i = 0; i < n; ++i)
+ if (name.equals(ica.innerClass(i)))
+ return classPool.get(ica.outerClass(i));
+
+ return null;
+ }
+
+ public CtClass makeNestedClass(String name, boolean isStatic) {
+ if (!isStatic)
+ throw new RuntimeException(
+ "sorry, only nested static class is supported");
+
+ checkModify();
+ CtClass c = classPool.makeNestedClass(getName() + "$" + name);
+ ClassFile cf = getClassFile2();
+ InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute(
+ InnerClassesAttribute.tag);
+ if (ica == null) {
+ ica = new InnerClassesAttribute(cf.getConstPool());
+ cf.addAttribute(ica);
+ }
+
+ ica.append(c.getName(), this.getName(), name, AccessFlag.STATIC);
+ ClassFile cf2 = c.getClassFile2();
+ cf2.addAttribute(ica.copy(cf2.getConstPool(), null));
+ return c;
+ }
+
+ public CtField[] getFields() {
+ ArrayList alist = new ArrayList();
+ getFields(alist, this);
+ return (CtField[])alist.toArray(new CtField[alist.size()]);
+ }
+
+ private static void getFields(ArrayList alist, CtClass cc) {
+ int i, num;
+ if (cc == null)
+ return;
+
+ try {
+ getFields(alist, cc.getSuperclass());
+ }
+ catch (NotFoundException e) {}
+
+ try {
+ CtClass[] ifs = cc.getInterfaces();
+ num = ifs.length;
+ for (i = 0; i < num; ++i)
+ getFields(alist, ifs[i]);
+ }
+ catch (NotFoundException e) {}
+
+ CtField cf = ((CtClassType)cc).getFieldsCache();
+ while (cf != null) {
+ if (Modifier.isPublic(cf.getModifiers()))
+ alist.add(cf);
+
+ cf = cf.next;
+ }
+ }
+
+ public CtField getField(String name) throws NotFoundException {
+ CtField f = getField2(name);
+ if (f == null)
+ throw new NotFoundException("field: " + name + " in " + getName());
+ else
+ return f;
+ }
+
+ CtField getField2(String name) {
+ CtField df = getDeclaredField2(name);
+ if (df != null)
+ return df;
+
+ try {
+ CtClass[] ifs = getInterfaces();
+ int num = ifs.length;
+ for (int i = 0; i < num; ++i) {
+ CtField f = ifs[i].getField2(name);
+ if (f != null)
+ return f;
+ }
+ CtClass s = getSuperclass();
+ if (s != null)
+ return s.getField2(name);
+ }
+ catch (NotFoundException e) {}
+ return null;
+ }
+
+ public CtField[] getDeclaredFields() {
+ CtField cf = getFieldsCache();
+ int num = CtField.count(cf);
+ CtField[] cfs = new CtField[num];
+ int i = 0;
+ while (cf != null) {
+ cfs[i++] = cf;
+ cf = cf.next;
+ }
+
+ return cfs;
+ }
+
+ protected CtField getFieldsCache() {
+ if (fieldsCache == null) {
+ List list = getClassFile2().getFields();
+ int n = list.size();
+ for (int i = 0; i < n; ++i) {
+ FieldInfo finfo = (FieldInfo)list.get(i);
+ fieldsCache = CtField.append(fieldsCache,
+ new CtField(finfo, this));
+ }
+ }
+
+ return fieldsCache;
+ }
+
+ public CtField getDeclaredField(String name) throws NotFoundException {
+ CtField f = getDeclaredField2(name);
+ if (f == null)
+ throw new NotFoundException("field: " + name + " in " + getName());
+ else
+ return f;
+ }
+
+ private CtField getDeclaredField2(String name) {
+ CtField cf = getFieldsCache();
+ while (cf != null) {
+ if (cf.getName().equals(name))
+ return cf;
+
+ cf = cf.next;
+ }
+
+ return null;
+ }
+
+ public CtBehavior[] getDeclaredBehaviors() {
+ CtConstructor cc = getConstructorsCache();
+ CtMethod cm = getMethodsCache();
+ int num = CtMethod.count(cm) + CtConstructor.count(cc);
+ CtBehavior[] cb = new CtBehavior[num];
+ int i = 0;
+ while (cc != null) {
+ cb[i++] = cc;
+ cc = cc.next;
+ }
+
+ while (cm != null) {
+ cb[i++] = cm;
cm = cm.next;
- }
- }
- }
-
- public CtMethod getMethod(String name, String desc)
- throws NotFoundException
- {
- CtMethod m = getMethod0(this, name, desc);
- if (m != null)
- return m;
- else
- throw new NotFoundException(name + "(..) is not found in "
- + getName());
- }
-
- private static CtMethod getMethod0(CtClass cc,
- String name, String desc)
- {
- if (cc instanceof CtClassType)
- {
- CtMethod cm = ((CtClassType) cc).getMethodsCache();
- while (cm != null)
- {
- if (cm.getName().equals(name)
+ }
+
+ return cb;
+ }
+
+ public CtConstructor[] getConstructors() {
+ CtConstructor[] cons = getDeclaredConstructors();
+ if (cons.length == 0)
+ return cons;
+
+ int n = 0;
+ int i = cons.length;
+ while (--i >= 0)
+ if (Modifier.isPublic(cons[i].getModifiers()))
+ ++n;
+
+ CtConstructor[] result = new CtConstructor[n];
+ n = 0;
+ i = cons.length;
+ while (--i >= 0) {
+ CtConstructor c = cons[i];
+ if (Modifier.isPublic(c.getModifiers()))
+ result[n++] = c;
+ }
+
+ return result;
+ }
+
+ public CtConstructor getConstructor(String desc)
+ throws NotFoundException
+ {
+ CtConstructor cc = getConstructorsCache();
+ while (cc != null) {
+ if (cc.getMethodInfo2().getDescriptor().equals(desc))
+ return cc;
+
+ cc = cc.next;
+ }
+
+ return super.getConstructor(desc);
+ }
+
+ public CtConstructor[] getDeclaredConstructors() {
+ CtConstructor cc = getConstructorsCache();
+ int num = CtConstructor.count(cc);
+ CtConstructor[] ccs = new CtConstructor[num];
+ int i = 0;
+ while (cc != null) {
+ ccs[i++] = cc;
+ cc = cc.next;
+ }
+
+ return ccs;
+ }
+
+ protected CtConstructor getConstructorsCache() {
+ if (constructorsCache == null) {
+ List list = getClassFile2().getMethods();
+ int n = list.size();
+ for (int i = 0; i < n; ++i) {
+ MethodInfo minfo = (MethodInfo)list.get(i);
+ if (minfo.isConstructor())
+ constructorsCache
+ = CtConstructor.append(constructorsCache,
+ new CtConstructor(minfo, this));
+ }
+ }
+
+ return constructorsCache;
+ }
+
+ public CtConstructor getClassInitializer() {
+ if (classInitializerCache == null) {
+ MethodInfo minfo = getClassFile2().getStaticInitializer();
+ if (minfo != null)
+ classInitializerCache = new CtConstructor(minfo, this);
+ }
+
+ return classInitializerCache;
+ }
+
+ public CtMethod[] getMethods() {
+ HashMap h = new HashMap();
+ getMethods0(h, this);
+ return (CtMethod[])h.values().toArray(new CtMethod[0]);
+ }
+
+ private static void getMethods0(HashMap h, CtClass cc) {
+ try {
+ CtClass[] ifs = cc.getInterfaces();
+ int size = ifs.length;
+ for (int i = 0; i < size; ++i)
+ getMethods0(h, ifs[i]);
+ }
+ catch (NotFoundException e) {}
+
+ try {
+ CtClass s = cc.getSuperclass();
+ if (s != null)
+ getMethods0(h, s);
+ }
+ catch (NotFoundException e) {}
+
+ if (cc instanceof CtClassType) {
+ CtMethod cm = ((CtClassType)cc).getMethodsCache();
+ while (cm != null) {
+ if (Modifier.isPublic(cm.getModifiers()))
+ h.put(cm.getStringRep(), cm);
+
+ cm = cm.next;
+ }
+ }
+ }
+
+ public CtMethod getMethod(String name, String desc)
+ throws NotFoundException
+ {
+ CtMethod m = getMethod0(this, name, desc);
+ if (m != null)
+ return m;
+ else
+ throw new NotFoundException(name + "(..) is not found in "
+ + getName());
+ }
+
+ private static CtMethod getMethod0(CtClass cc,
+ String name, String desc) {
+ if (cc instanceof CtClassType) {
+ CtMethod cm = ((CtClassType)cc).getMethodsCache();
+ while (cm != null) {
+ if (cm.getName().equals(name)
&& cm.getMethodInfo2().getDescriptor().equals(desc))
- return cm;
+ return cm;
+ cm = cm.next;
+ }
+ }
+
+ try {
+ CtClass s = cc.getSuperclass();
+ if (s != null) {
+ CtMethod m = getMethod0(s, name, desc);
+ if (m != null)
+ return m;
+ }
+ }
+ catch (NotFoundException e) {}
+
+ try {
+ CtClass[] ifs = cc.getInterfaces();
+ int size = ifs.length;
+ for (int i = 0; i < size; ++i) {
+ CtMethod m = getMethod0(ifs[i], name, desc);
+ if (m != null)
+ return m;
+ }
+ }
+ catch (NotFoundException e) {}
+ return null;
+ }
+
+ public CtMethod[] getDeclaredMethods() {
+ CtMethod cm = getMethodsCache();
+ int num = CtMethod.count(cm);
+ CtMethod[] cms = new CtMethod[num];
+ int i = 0;
+ while (cm != null) {
+ cms[i++] = cm;
cm = cm.next;
- }
- }
-
- try
- {
- CtClass s = cc.getSuperclass();
- if (s != null)
- {
- CtMethod m = getMethod0(s, name, desc);
- if (m != null)
- return m;
- }
- }
- catch (NotFoundException e)
- {
- }
-
- try
- {
- CtClass[] ifs = cc.getInterfaces();
- int size = ifs.length;
- for (int i = 0; i < size; ++i)
- {
- CtMethod m = getMethod0(ifs[i], name, desc);
- if (m != null)
- return m;
- }
- }
- catch (NotFoundException e)
- {
- }
- return null;
- }
-
- public CtMethod[] getDeclaredMethods()
- {
- CtMethod cm = getMethodsCache();
- int num = CtMethod.count(cm);
- CtMethod[] cms = new CtMethod[num];
- int i = 0;
- while (cm != null)
- {
- cms[i++] = cm;
- cm = cm.next;
- }
-
- return cms;
- }
-
- public CtMethod getDeclaredMethod(String name) throws NotFoundException
- {
- CtMethod m = getMethodsCache();
- while (m != null)
- {
- if (m.getName().equals(name))
- return m;
-
- m = m.next;
- }
-
- throw new NotFoundException(name + "(..) is not found in "
- + getName());
- }
-
- public CtMethod getDeclaredMethod(String name, CtClass[] params)
- throws NotFoundException
- {
- String desc = Descriptor.ofParameters(params);
- CtMethod m = getMethodsCache();
- while (m != null)
- {
- if (m.getName().equals(name)
- && m.getMethodInfo2().getDescriptor().startsWith(desc))
- return m;
+ }
+
+ return cms;
+ }
+
+ public CtMethod getDeclaredMethod(String name) throws NotFoundException {
+ CtMethod m = getMethodsCache();
+ while (m != null) {
+ if (m.getName().equals(name))
+ return m;
+
+ m = m.next;
+ }
+
+ throw new NotFoundException(name + "(..) is not found in "
+ + getName());
+ }
+
+ public CtMethod getDeclaredMethod(String name, CtClass[] params)
+ throws NotFoundException
+ {
+ String desc = Descriptor.ofParameters(params);
+ CtMethod m = getMethodsCache();
+ while (m != null) {
+ if (m.getName().equals(name)
+ && m.getMethodInfo2().getDescriptor().startsWith(desc))
+ return m;
+
+ m = m.next;
+ }
+
+ throw new NotFoundException(name + "(..) is not found in "
+ + getName());
+ }
+
+ protected CtMethod getMethodsCache() {
+ if (methodsCache == null) {
+ List list = getClassFile2().getMethods();
+ int n = list.size();
+ for (int i = 0; i < n; ++i) {
+ MethodInfo minfo = (MethodInfo)list.get(i);
+ if (minfo.isMethod())
+ methodsCache = CtMethod.append(methodsCache,
+ new CtMethod(minfo, this));
+ }
+ }
+
+ return methodsCache;
+ }
+
+ public void addField(CtField f, String init)
+ throws CannotCompileException
+ {
+ addField(f, CtField.Initializer.byExpr(init));
+ }
+
+ public void addField(CtField f, CtField.Initializer init)
+ throws CannotCompileException
+ {
+ checkModify();
+ if (f.getDeclaringClass() != this)
+ throw new CannotCompileException("cannot add");
+
+ if (init == null)
+ init = f.getInit();
+
+ getFieldsCache();
+ fieldsCache = CtField.append(fieldsCache, f);
+ getClassFile2().addField(f.getFieldInfo2());
+
+ if (init != null) {
+ FieldInitLink fil = new FieldInitLink(f, init);
+ FieldInitLink link = fieldInitializers;
+ if (link == null)
+ fieldInitializers = fil;
+ else {
+ while (link.next != null)
+ link = link.next;
+
+ link.next = fil;
+ }
+ }
+ }
+
+ public CtConstructor makeClassInitializer()
+ throws CannotCompileException
+ {
+ CtConstructor clinit = getClassInitializer();
+ if (clinit != null)
+ return clinit;
+
+ checkModify();
+ ClassFile cf = getClassFile2();
+ Bytecode code = new Bytecode(cf.getConstPool(), 0, 0);
+ modifyClassConstructor(cf, code, 0, 0);
+ return getClassInitializer();
+ }
+
+ public void addConstructor(CtConstructor c)
+ throws CannotCompileException
+ {
+ checkModify();
+ if (c.getDeclaringClass() != this)
+ throw new CannotCompileException("cannot add");
+
+ getConstructorsCache();
+ constructorsCache = CtConstructor.append(constructorsCache, c);
+ getClassFile2().addMethod(c.getMethodInfo2());
+ }
+
+ public void addMethod(CtMethod m) throws CannotCompileException {
+ checkModify();
+ if (m.getDeclaringClass() != this)
+ throw new CannotCompileException("cannot add");
+
+ getMethodsCache();
+ methodsCache = CtMethod.append(methodsCache, m);
+ getClassFile2().addMethod(m.getMethodInfo2());
+ if ((m.getModifiers() & Modifier.ABSTRACT) != 0)
+ setModifiers(getModifiers() | Modifier.ABSTRACT);
+ }
+
+ public byte[] getAttribute(String name) {
+ AttributeInfo ai = getClassFile2().getAttribute(name);
+ if (ai == null)
+ return null;
+ else
+ return ai.get();
+ }
+
+ public void setAttribute(String name, byte[] data) {
+ checkModify();
+ ClassFile cf = getClassFile2();
+ cf.addAttribute(new AttributeInfo(cf.getConstPool(), name, data));
+ }
+
+ public void instrument(CodeConverter converter)
+ throws CannotCompileException
+ {
+ checkModify();
+ ClassFile cf = getClassFile2();
+ ConstPool cp = cf.getConstPool();
+ List list = cf.getMethods();
+ int n = list.size();
+ for (int i = 0; i < n; ++i) {
+ MethodInfo minfo = (MethodInfo)list.get(i);
+ converter.doit(this, minfo, cp);
+ }
+ }
+
+ public void instrument(ExprEditor editor)
+ throws CannotCompileException
+ {
+ checkModify();
+ ClassFile cf = getClassFile2();
+ List list = cf.getMethods();
+ int n = list.size();
+ for (int i = 0; i < n; ++i) {
+ MethodInfo minfo = (MethodInfo)list.get(i);
+ editor.doit(this, minfo);
+ }
+ }
+
+ public void toBytecode(DataOutputStream out)
+ throws CannotCompileException, IOException
+ {
+ try {
+ if (isModified()) {
+ checkPruned("toBytecode");
+ ClassFile cf = getClassFile2();
+ modifyClassConstructor(cf);
+ modifyConstructors(cf);
+ cf.write(out);
+ out.flush();
+ fieldInitializers = null;
+ if (doPruning) {
+ // to save memory
+ cf.prune();
+ wasPruned = true;
+ }
+ }
+ else {
+ classPool.writeClassfile(getName(), out);
+ // to save memory
+ eraseCache();
+ classfile = null;
+ }
- m = m.next;
- }
-
- throw new NotFoundException(name + "(..) is not found in "
- + getName());
- }
-
- protected CtMethod getMethodsCache()
- {
- if (methodsCache == null)
- {
- List list = getClassFile2().getMethods();
- int n = list.size();
- for (int i = 0; i < n; ++i)
- {
- MethodInfo minfo = (MethodInfo) list.get(i);
- if (minfo.isMethod())
- methodsCache = CtMethod.append(methodsCache,
- new CtMethod(minfo, this));
- }
- }
-
- return methodsCache;
- }
-
- public void addField(CtField f, String init)
- throws CannotCompileException
- {
- addField(f, CtField.Initializer.byExpr(init));
- }
-
- public void addField(CtField f, CtField.Initializer init)
- throws CannotCompileException
- {
- checkModify();
- if (f.getDeclaringClass() != this)
- throw new CannotCompileException("cannot add");
-
- if (init == null)
- init = f.getInit();
-
- getFieldsCache();
- fieldsCache = CtField.append(fieldsCache, f);
- getClassFile2().addField(f.getFieldInfo2());
-
- if (init != null)
- {
- FieldInitLink fil = new FieldInitLink(f, init);
- FieldInitLink link = fieldInitializers;
- if (link == null)
- fieldInitializers = fil;
- else
- {
- while (link.next != null)
- link = link.next;
-
- link.next = fil;
- }
- }
- }
-
- public CtConstructor makeClassInitializer()
- throws CannotCompileException
- {
- CtConstructor clinit = getClassInitializer();
- if (clinit != null)
- return clinit;
-
- checkModify();
- ClassFile cf = getClassFile2();
- Bytecode code = new Bytecode(cf.getConstPool(), 0, 0);
- modifyClassConstructor(cf, code, 0, 0);
- return getClassInitializer();
- }
-
- public void addConstructor(CtConstructor c)
- throws CannotCompileException
- {
- checkModify();
- if (c.getDeclaringClass() != this)
- throw new CannotCompileException("cannot add");
-
- getConstructorsCache();
- constructorsCache = CtConstructor.append(constructorsCache, c);
- getClassFile2().addMethod(c.getMethodInfo2());
- }
-
- public void addMethod(CtMethod m) throws CannotCompileException
- {
- checkModify();
- if (m.getDeclaringClass() != this)
- throw new CannotCompileException("cannot add");
-
- getMethodsCache();
- methodsCache = CtMethod.append(methodsCache, m);
- getClassFile2().addMethod(m.getMethodInfo2());
- if ((m.getModifiers() & Modifier.ABSTRACT) != 0)
- setModifiers(getModifiers() | Modifier.ABSTRACT);
- }
-
- public byte[] getAttribute(String name)
- {
- AttributeInfo ai = getClassFile2().getAttribute(name);
- if (ai == null)
- return null;
- else
- return ai.get();
- }
-
- public void setAttribute(String name, byte[] data)
- {
- checkModify();
- ClassFile cf = getClassFile2();
- cf.addAttribute(new AttributeInfo(cf.getConstPool(), name, data));
- }
-
- public void instrument(CodeConverter converter)
- throws CannotCompileException
- {
- checkModify();
- ClassFile cf = getClassFile2();
- ConstPool cp = cf.getConstPool();
- List list = cf.getMethods();
- int n = list.size();
- for (int i = 0; i < n; ++i)
- {
- MethodInfo minfo = (MethodInfo) list.get(i);
- converter.doit(this, minfo, cp);
- }
- }
-
- public void instrument(ExprEditor editor)
- throws CannotCompileException
- {
- checkModify();
- ClassFile cf = getClassFile2();
- List list = cf.getMethods();
- int n = list.size();
- for (int i = 0; i < n; ++i)
- {
- MethodInfo minfo = (MethodInfo) list.get(i);
- editor.doit(this, minfo);
- }
- }
-
- public void toBytecode(DataOutputStream out)
- throws CannotCompileException, IOException
- {
- try
- {
- if (isModified())
- {
- checkPruned("toBytecode");
- ClassFile cf = getClassFile2();
- modifyClassConstructor(cf);
- modifyConstructors(cf);
- cf.write(out);
- out.flush();
- fieldInitializers = null;
- if (doPruning)
- {
- // to save memory
- cf.prune();
- wasPruned = true;
+ wasFrozen = true;
+ }
+ catch (NotFoundException e) {
+ throw new CannotCompileException(e);
+ }
+ catch (IOException e) {
+ throw new CannotCompileException(e);
+ }
+ }
+
+ private void checkPruned(String method) {
+ if (wasPruned)
+ throw new RuntimeException(method + "(): " + getName()
+ + " was pruned.");
+ }
+
+ public void stopPruning(boolean stop) {
+ doPruning = !stop;
+ }
+
+ private void modifyClassConstructor(ClassFile cf)
+ throws CannotCompileException, NotFoundException
+ {
+ if (fieldInitializers == null)
+ return;
+
+ Bytecode code = new Bytecode(cf.getConstPool(), 0, 0);
+ Javac jv = new Javac(code, this);
+ int stacksize = 0;
+ boolean doInit = false;
+ for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) {
+ CtField f = fi.field;
+ if (Modifier.isStatic(f.getModifiers())) {
+ doInit = true;
+ int s = fi.init.compileIfStatic(f.getType(), f.getName(),
+ code, jv);
+ if (stacksize < s)
+ stacksize = s;
}
- }
- else
- {
- classPool.writeClassfile(getName(), out);
- // to save memory
- eraseCache();
- classfile = null;
- }
-
- wasFrozen = true;
- }
- catch (NotFoundException e)
- {
- throw new CannotCompileException(e);
- }
- catch (IOException e)
- {
- throw new CannotCompileException(e);
- }
- }
-
- private void checkPruned(String method)
- {
- if (wasPruned)
- throw new RuntimeException(method + "(): " + getName()
- + " was pruned.");
- }
-
- public void stopPruning(boolean stop)
- {
- doPruning = !stop;
- }
-
- private void modifyClassConstructor(ClassFile cf)
- throws CannotCompileException, NotFoundException
- {
- if (fieldInitializers == null)
- return;
-
- Bytecode code = new Bytecode(cf.getConstPool(), 0, 0);
- Javac jv = new Javac(code, this);
- int stacksize = 0;
- boolean doInit = false;
- for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next)
- {
- CtField f = fi.field;
- if (Modifier.isStatic(f.getModifiers()))
- {
- doInit = true;
- int s = fi.init.compileIfStatic(f.getType(), f.getName(),
- code, jv);
- if (stacksize < s)
- stacksize = s;
- }
- }
-
- if (doInit) // need an initializer for static fileds.
- modifyClassConstructor(cf, code, stacksize, 0);
- }
-
- private void modifyClassConstructor(ClassFile cf, Bytecode code,
- int stacksize, int localsize)
- throws CannotCompileException
- {
- MethodInfo m = cf.getStaticInitializer();
- if (m == null)
- {
- code.add(Bytecode.RETURN);
- code.setMaxStack(stacksize);
- code.setMaxLocals(localsize);
- m = new MethodInfo(cf.getConstPool(), "<clinit>", "()V");
- m.setAccessFlags(AccessFlag.STATIC);
- m.setCodeAttribute(code.toCodeAttribute());
- cf.addMethod(m);
- }
- else
- {
- CodeAttribute codeAttr = m.getCodeAttribute();
- if (codeAttr == null)
- throw new CannotCompileException("empty <clinit>");
-
- try
- {
- CodeIterator it = codeAttr.iterator();
- int pos = it.insertEx(code.get());
- it.insert(code.getExceptionTable(), pos);
- int maxstack = codeAttr.getMaxStack();
- if (maxstack < stacksize)
- codeAttr.setMaxStack(stacksize);
-
- int maxlocals = codeAttr.getMaxLocals();
- if (maxlocals < localsize)
- codeAttr.setMaxLocals(localsize);
- }
- catch (BadBytecode e)
- {
+ }
+
+ if (doInit) // need an initializer for static fileds.
+ modifyClassConstructor(cf, code, stacksize, 0);
+ }
+
+ private void modifyClassConstructor(ClassFile cf, Bytecode code,
+ int stacksize, int localsize)
+ throws CannotCompileException
+ {
+ MethodInfo m = cf.getStaticInitializer();
+ if (m == null) {
+ code.add(Bytecode.RETURN);
+ code.setMaxStack(stacksize);
+ code.setMaxLocals(localsize);
+ m = new MethodInfo(cf.getConstPool(), "<clinit>", "()V");
+ m.setAccessFlags(AccessFlag.STATIC);
+ m.setCodeAttribute(code.toCodeAttribute());
+ cf.addMethod(m);
+ }
+ else {
+ CodeAttribute codeAttr = m.getCodeAttribute();
+ if (codeAttr == null)
+ throw new CannotCompileException("empty <clinit>");
+
+ try {
+ CodeIterator it = codeAttr.iterator();
+ int pos = it.insertEx(code.get());
+ it.insert(code.getExceptionTable(), pos);
+ int maxstack = codeAttr.getMaxStack();
+ if (maxstack < stacksize)
+ codeAttr.setMaxStack(stacksize);
+
+ int maxlocals = codeAttr.getMaxLocals();
+ if (maxlocals < localsize)
+ codeAttr.setMaxLocals(localsize);
+ }
+ catch (BadBytecode e) {
+ throw new CannotCompileException(e);
+ }
+ }
+ }
+
+ private void modifyConstructors(ClassFile cf)
+ throws CannotCompileException, NotFoundException
+ {
+ if (fieldInitializers == null)
+ return;
+
+ ConstPool cp = cf.getConstPool();
+ List list = cf.getMethods();
+ int n = list.size();
+ for (int i = 0; i < n; ++i) {
+ MethodInfo minfo = (MethodInfo)list.get(i);
+ if (minfo.isConstructor()) {
+ CodeAttribute codeAttr = minfo.getCodeAttribute();
+ if (codeAttr != null)
+ try {
+ Bytecode init = new Bytecode(cp, 0,
+ codeAttr.getMaxLocals());
+ CtClass[] params
+ = Descriptor.getParameterTypes(
+ minfo.getDescriptor(),
+ classPool);
+ int stacksize = makeFieldInitializer(init, params);
+ insertAuxInitializer(codeAttr, init, stacksize);
+ }
+ catch (BadBytecode e) {
+ throw new CannotCompileException(e);
+ }
+ }
+ }
+ }
+
+ private static void insertAuxInitializer(CodeAttribute codeAttr,
+ Bytecode initializer,
+ int stacksize)
+ throws BadBytecode
+ {
+ CodeIterator it = codeAttr.iterator();
+ int index = it.skipSuperConstructor();
+ if (index < 0) {
+ index = it.skipThisConstructor();
+ if (index >= 0)
+ return; // this() is called.
+
+ // Neither this() or super() is called.
+ }
+
+ int pos = it.insertEx(initializer.get());
+ it.insert(initializer.getExceptionTable(), pos);
+ int maxstack = codeAttr.getMaxStack();
+ if (maxstack < stacksize)
+ codeAttr.setMaxStack(stacksize);
+ }
+
+ private int makeFieldInitializer(Bytecode code, CtClass[] parameters)
+ throws CannotCompileException, NotFoundException
+ {
+ int stacksize = 0;
+ Javac jv = new Javac(code, this);
+ try {
+ jv.recordParams(parameters, false);
+ }
+ catch (CompileError e) {
throw new CannotCompileException(e);
- }
- }
- }
-
- private void modifyConstructors(ClassFile cf)
- throws CannotCompileException, NotFoundException
- {
- if (fieldInitializers == null)
- return;
-
- ConstPool cp = cf.getConstPool();
- List list = cf.getMethods();
- int n = list.size();
- for (int i = 0; i < n; ++i)
- {
- MethodInfo minfo = (MethodInfo) list.get(i);
- if (minfo.isConstructor())
- {
- CodeAttribute codeAttr = minfo.getCodeAttribute();
- if (codeAttr != null)
- try
- {
- Bytecode init = new Bytecode(cp, 0,
- codeAttr.getMaxLocals());
- CtClass[] params
- = Descriptor.getParameterTypes(minfo.getDescriptor(),
- classPool);
- int stacksize = makeFieldInitializer(init, params);
- insertAuxInitializer(codeAttr, init, stacksize);
- }
- catch (BadBytecode e)
- {
- throw new CannotCompileException(e);
- }
- }
- }
- }
-
- private static void insertAuxInitializer(CodeAttribute codeAttr,
- Bytecode initializer,
- int stacksize)
- throws BadBytecode
- {
- CodeIterator it = codeAttr.iterator();
- int index = it.skipSuperConstructor();
- if (index < 0)
- {
- index = it.skipThisConstructor();
- if (index >= 0)
- return; // this() is called.
-
- // Neither this() or super() is called.
- }
-
- int pos = it.insertEx(initializer.get());
- it.insert(initializer.getExceptionTable(), pos);
- int maxstack = codeAttr.getMaxStack();
- if (maxstack < stacksize)
- codeAttr.setMaxStack(stacksize);
- }
-
- private int makeFieldInitializer(Bytecode code, CtClass[] parameters)
- throws CannotCompileException, NotFoundException
- {
- int stacksize = 0;
- Javac jv = new Javac(code, this);
- try
- {
- jv.recordParams(parameters, false);
- }
- catch (CompileError e)
- {
- throw new CannotCompileException(e);
- }
-
- for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next)
- {
- CtField f = fi.field;
- if (!Modifier.isStatic(f.getModifiers()))
- {
- int s = fi.init.compile(f.getType(), f.getName(), code,
- parameters, jv);
- if (stacksize < s)
- stacksize = s;
- }
- }
-
- return stacksize;
- }
-
- // Methods used by CtNewWrappedMethod
-
- Hashtable getHiddenMethods()
- {
- if (hiddenMethods == null)
- hiddenMethods = new Hashtable();
-
- return hiddenMethods;
- }
-
- int getUniqueNumber()
- {
- return uniqueNumberSeed++;
- }
+ }
+
+ for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) {
+ CtField f = fi.field;
+ if (!Modifier.isStatic(f.getModifiers())) {
+ int s = fi.init.compile(f.getType(), f.getName(), code,
+ parameters, jv);
+ if (stacksize < s)
+ stacksize = s;
+ }
+ }
+
+ return stacksize;
+ }
+
+ // Methods used by CtNewWrappedMethod
+
+ Hashtable getHiddenMethods() {
+ if (hiddenMethods == null)
+ hiddenMethods = new Hashtable();
+
+ return hiddenMethods;
+ }
+
+ int getUniqueNumber() { return uniqueNumberSeed++; }
}
-class FieldInitLink
-{
- FieldInitLink next;
- CtField field;
- CtField.Initializer init;
-
- FieldInitLink(CtField f, CtField.Initializer i)
- {
- next = null;
- field = f;
- init = i;
- }
+class FieldInitLink {
+ FieldInitLink next;
+ CtField field;
+ CtField.Initializer init;
+
+ FieldInitLink(CtField f, CtField.Initializer i) {
+ next = null;
+ field = f;
+ init = i;
+ }
}
package javassist.bytecode;
-import javassist.bytecode.annotation.Annotation;
-import javassist.bytecode.annotation.AnnotationMemberValue;
-import javassist.bytecode.annotation.AnnotationsWriter;
-import javassist.bytecode.annotation.ArrayMemberValue;
-import javassist.bytecode.annotation.BooleanMemberValue;
-import javassist.bytecode.annotation.ByteMemberValue;
-import javassist.bytecode.annotation.CharMemberValue;
-import javassist.bytecode.annotation.ClassMemberValue;
-import javassist.bytecode.annotation.DoubleMemberValue;
-import javassist.bytecode.annotation.EnumMemberValue;
-import javassist.bytecode.annotation.FloatMemberValue;
-import javassist.bytecode.annotation.IntegerMemberValue;
-import javassist.bytecode.annotation.LongMemberValue;
-import javassist.bytecode.annotation.MemberValue;
-import javassist.bytecode.annotation.ShortMemberValue;
-import javassist.bytecode.annotation.StringMemberValue;
-
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.util.HashMap;
import java.util.Map;
+import java.io.IOException;
+import java.io.DataInputStream;
+import java.io.ByteArrayOutputStream;
+import javassist.bytecode.annotation.*;
/**
* A class representing
* <code>RuntimeVisibleAnnotations_attribute</code> and
* <code>RuntimeInvisibleAnnotations_attribute</code>.
- * <p/>
+ *
* <p>To obtain an AnnotationAttribute object, invoke
* <code>getAttribute(AnnotationsAttribute.invisibleTag)</code>
* in <code>ClassFile</code>, <code>MethodInfo</code>,
* If the parameter is
* <code>AnnotationAttribute.visibleTag</code>, then the obtained
* attribute is a runtime visible one.
- * <p/>
+ *
* <p>If you want to record a new AnnotationAttribute object, execute the
* following snippet:
- * <p/>
+ *
* <ul><pre>
* ClassFile cf = ... ;
* ConstPool cp = cf.getConstPool();
* cf.addAttribute(attr);
* </pre></ul>
*/
-public class AnnotationsAttribute extends AttributeInfo
-{
- /**
- * The name of the <code>RuntimeVisibleAnnotations</code> attribute.
- */
- public static final String visibleTag = "RuntimeVisibleAnnotations";
-
- /**
- * The name of the <code>RuntimeInvisibleAnnotations</code> attribute.
- */
- public static final String invisibleTag = "RuntimeInvisibleAnnotations";
-
- private HashMap annotationMap;
-
- /**
- * Constructs a <code>Runtime(In)VisisbleAnnotations_attribute</code>.
- *
- * @param cp constant pool
- * @param attrname attribute name (<code>visibleTag</code> or
- * <code>invisibleTag</code>).
- * @param info the contents of this attribute. It does not
- * include <code>attribute_name_index</code> or
- * <code>attribute_length</code>.
- */
- public AnnotationsAttribute(ConstPool cp, String attrname, byte[] info)
- {
- super(cp, attrname, info);
- }
-
- /**
- * Constructs an empty
- * <code>Runtime(In)VisisbleAnnotations_attribute</code>.
- * A new annotation can be later added to the created attribute
- * by <code>setAnnotations()</code>.
- *
- * @param cp constant pool
- * @param attrname attribute name (<code>visibleTag</code> or
- * <code>invisibleTag</code>).
- * @see #setAnnotations(Annotation[])
- */
- public AnnotationsAttribute(ConstPool cp, String attrname)
- {
- this(cp, attrname, new byte[]{0, 0});
- }
-
- /**
- * @param n the attribute name.
- */
- AnnotationsAttribute(ConstPool cp, int n, DataInputStream in)
- throws IOException
- {
- super(cp, n, in);
- }
-
- /**
- * Returns <code>num_annotations</code>.
- */
- public int numAnnotations()
- {
- return ByteArray.readU16bit(info, 0);
- }
-
- /**
- * Copies this attribute and returns a new copy.
- */
- public AttributeInfo copy(ConstPool newCp, Map classnames)
- {
- Copier copier = new Copier(info, constPool, newCp, classnames);
- try
- {
- copier.annotationArray();
- return new AnnotationsAttribute(newCp, getName(), copier.close());
- }
- catch (Exception e)
- {
- throw new RuntimeException(e.toString());
- }
- }
-
- public Annotation getAnnotation(String type)
- {
- Annotation[] annotations = getAnnotations();
- for (int i = 0; i < annotations.length; i++)
- {
- if (annotations[i].getTypeName().equals(type)) return annotations[i];
- }
- return null;
- }
-
- public void addAnnotation(Annotation annotation)
- {
- String type = annotation.getTypeName();
- Annotation[] annotations = getAnnotations();
- for (int i = 0; i < annotations.length; i++)
- {
- if (annotations[i].getTypeName().equals(type))
- {
- annotations[i] = annotation;
- setAnnotations(annotations);
- return;
- }
- }
- Annotation[] newlist = new Annotation[annotations.length + 1];
- System.arraycopy(annotations, 0, newlist, 0, annotations.length);
- newlist[annotations.length] = annotation;
- setAnnotations(newlist);
- }
-
- /**
- * Parses the annotations and returns a data structure representing
- * that parsed annotations. Note that changes of the node values of the
- * returned tree are not reflected on the annotations represented by
- * this object unless the tree is copied back to this object by
- * <code>setAnnotations()</code>.
- *
- * @see #setAnnotations(Annotation[])
- */
- public Annotation[] getAnnotations()
- {
- try
- {
- return new Parser(info, constPool).parseAnnotations();
- }
- catch (Exception e)
- {
- throw new RuntimeException(e.toString());
- }
- }
-
- /**
- * Changes the annotations represented by this object according to
- * the given array of <code>Annotation</code> objects.
- *
- * @param annotations the data structure representing the
- * new annotations.
- */
- public void setAnnotations(Annotation[] annotations)
- {
- ByteArrayOutputStream output = new ByteArrayOutputStream();
- AnnotationsWriter writer = new AnnotationsWriter(output, constPool);
- try
- {
- int n = annotations.length;
- writer.numAnnotations(n);
- for (int i = 0; i < n; ++i)
- annotations[i].write(writer);
-
- writer.close();
- }
- catch (IOException e)
- {
- throw new RuntimeException(e); // should never reach here.
- }
-
- set(output.toByteArray());
- }
-
- /**
- * Changes the annotations. A call to this method is equivalent to:
- * <ul><pre>setAnnotations(new Annotation[] { annotation })</pre></ul>
- *
- * @param annotation the data structure representing
- * the new annotation.
- */
- public void setAnnotation(Annotation annotation)
- {
- setAnnotations(new Annotation[]{annotation});
- }
-
- /**
- * Returns a string representation of this object.
- */
- public String toString()
- {
- Annotation[] a = getAnnotations();
- StringBuffer sbuf = new StringBuffer();
- int i = 0;
- while (i < a.length)
- {
- sbuf.append(a[i++].toString());
- if (i != a.length)
- sbuf.append(", ");
- }
-
- return sbuf.toString();
- }
-
- static class Walker
- {
- byte[] info;
-
- Walker(byte[] attrInfo)
- {
- info = attrInfo;
- }
-
- final void parameters() throws Exception
- {
- int numParam = info[0] & 0xff;
- parameters(numParam, 1);
- }
-
- void parameters(int numParam, int pos) throws Exception
- {
- for (int i = 0; i < numParam; ++i)
- pos = annotationArray(pos);
- }
-
- final void annotationArray() throws Exception
- {
- annotationArray(0);
- }
-
- final int annotationArray(int pos) throws Exception
- {
- int num = ByteArray.readU16bit(info, pos);
- return annotationArray(pos + 2, num);
- }
-
- int annotationArray(int pos, int num) throws Exception
- {
- for (int i = 0; i < num; ++i)
- pos = annotation(pos);
-
- return pos;
- }
-
- final int annotation(int pos) throws Exception
- {
- int type = ByteArray.readU16bit(info, pos);
- int numPairs = ByteArray.readU16bit(info, pos + 2);
- return annotation(pos + 4, type, numPairs);
- }
-
- int annotation(int pos, int type, int numPairs) throws Exception
- {
- for (int j = 0; j < numPairs; ++j)
- pos = memberValuePair(pos);
-
- return pos;
- }
-
- final int memberValuePair(int pos) throws Exception
- {
- int nameIndex = ByteArray.readU16bit(info, pos);
- return memberValuePair(pos + 2, nameIndex);
- }
-
- int memberValuePair(int pos, int nameIndex) throws Exception
- {
- return memberValue(pos);
- }
-
- final int memberValue(int pos) throws Exception
- {
- int tag = info[pos] & 0xff;
- if (tag == 'e')
- {
- int typeNameIndex = ByteArray.readU16bit(info, pos + 1);
- int constNameIndex = ByteArray.readU16bit(info, pos + 3);
- enumMemberValue(typeNameIndex, constNameIndex);
- return pos + 5;
- }
- else if (tag == 'c')
- {
- int index = ByteArray.readU16bit(info, pos + 1);
- classMemberValue(index);
- return pos + 3;
- }
- else if (tag == '@')
- return annotationMemberValue(pos + 1);
- else if (tag == '[')
- {
- int num = ByteArray.readU16bit(info, pos + 1);
- return arrayMemberValue(pos + 3, num);
- }
- else
- { // primitive types or String.
- int index = ByteArray.readU16bit(info, pos + 1);
- constValueMember(tag, index);
- return pos + 3;
- }
- }
-
- void constValueMember(int tag, int index) throws Exception
- {
- }
-
- void enumMemberValue(int typeNameIndex, int constNameIndex)
- throws Exception
- {
- }
-
- void classMemberValue(int index) throws Exception
- {
- }
-
- int annotationMemberValue(int pos) throws Exception
- {
- return annotation(pos);
- }
-
- int arrayMemberValue(int pos, int num) throws Exception
- {
- for (int i = 0; i < num; ++i)
- {
- pos = memberValue(pos);
- }
-
- return pos;
- }
- }
-
- static class Copier extends Walker
- {
- ByteArrayOutputStream output;
- AnnotationsWriter writer;
- ConstPool srcPool, destPool;
- Map classnames;
-
- /**
- * Constructs a copier. This copier renames some class names
- * into the new names specified by <code>map</code> when it copies
- * an annotation attribute.
- *
- * @param info the source attribute.
- * @param src the constant pool of the source class.
- * @param dest the constant pool of the destination class.
- * @param map pairs of replaced and substituted class names.
- * It can be null.
- */
- Copier(byte[] info, ConstPool src, ConstPool dest, Map map)
- {
- super(info);
- output = new ByteArrayOutputStream();
- writer = new AnnotationsWriter(output, dest);
- srcPool = src;
- destPool = dest;
- classnames = map;
- }
-
- byte[] close() throws IOException
- {
- writer.close();
- return output.toByteArray();
- }
-
- void parameters(int numParam, int pos) throws Exception
- {
- writer.numParameters(numParam);
- super.parameters(numParam, pos);
- }
-
- int annotationArray(int pos, int num) throws Exception
- {
- writer.numAnnotations(num);
- return super.annotationArray(pos, num);
- }
-
- int annotation(int pos, int type, int numPairs) throws Exception
- {
- writer.annotation(copy(type), numPairs);
- return super.annotation(pos, type, numPairs);
- }
-
- int memberValuePair(int pos, int nameIndex) throws Exception
- {
- writer.memberValuePair(copy(nameIndex));
- return super.memberValuePair(pos, nameIndex);
- }
-
- void constValueMember(int tag, int index) throws Exception
- {
- writer.constValueIndex(tag, copy(index));
- super.constValueMember(tag, index);
- }
-
- void enumMemberValue(int typeNameIndex, int constNameIndex)
- throws Exception
- {
- writer.enumConstValue(copy(typeNameIndex), copy(constNameIndex));
- super.enumMemberValue(typeNameIndex, constNameIndex);
- }
-
- void classMemberValue(int index) throws Exception
- {
- writer.classInfoIndex(copy(index));
- super.classMemberValue(index);
- }
-
- int annotationMemberValue(int pos) throws Exception
- {
- writer.annotationValue();
- return super.annotationMemberValue(pos);
- }
-
- int arrayMemberValue(int pos, int num) throws Exception
- {
- writer.arrayValue(num);
- return super.arrayMemberValue(pos, num);
- }
-
- /**
- * Copies a constant pool entry into the destination constant pool
- * and returns the index of the copied entry.
- *
- * @param srcIndex the index of the copied entry into the source
- * constant pool.
- * @return the index of the copied item into the destination
- * constant pool.
- */
- int copy(int srcIndex)
- {
- return srcPool.copy(srcIndex, destPool, classnames);
- }
- }
-
- static class Parser extends Walker
- {
- ConstPool pool;
- Annotation[][] allParams; // all parameters
- Annotation[] allAnno; // all annotations
- Annotation currentAnno; // current annotation
- MemberValue memberValue;
-
- /**
- * Constructs a parser. This parser constructs a parse tree of
- * the annotations.
- *
- * @param info the attribute.
- * @param src the constant pool.
- */
- Parser(byte[] info, ConstPool cp)
- {
- super(info);
- pool = cp;
- }
-
- Annotation[][] parseParameters() throws Exception
- {
- parameters();
- return allParams;
- }
-
- Annotation[] parseAnnotations() throws Exception
- {
- annotationArray();
- return allAnno;
- }
-
- void parameters(int numParam, int pos) throws Exception
- {
- Annotation[][] params = new Annotation[numParam][];
- for (int i = 0; i < numParam; ++i)
- {
- pos = annotationArray(pos);
- params[i] = allAnno;
- }
-
- allParams = params;
- }
-
- int annotationArray(int pos, int num) throws Exception
- {
- Annotation[] array = new Annotation[num];
- for (int i = 0; i < num; ++i)
- {
- pos = annotation(pos);
- array[i] = currentAnno;
- }
-
- allAnno = array;
- return pos;
- }
-
- int annotation(int pos, int type, int numPairs) throws Exception
- {
- currentAnno = new Annotation(type, pool);
- return super.annotation(pos, type, numPairs);
- }
-
- int memberValuePair(int pos, int nameIndex) throws Exception
- {
- pos = super.memberValuePair(pos, nameIndex);
- currentAnno.addMemberValue(nameIndex, memberValue);
- return pos;
- }
-
- void constValueMember(int tag, int index) throws Exception
- {
- MemberValue m;
- ConstPool cp = pool;
- switch (tag)
- {
- case 'B':
- m = new ByteMemberValue(index, cp);
- break;
- case 'C':
- m = new CharMemberValue(index, cp);
- break;
- case 'D':
- m = new DoubleMemberValue(index, cp);
- break;
- case 'F':
- m = new FloatMemberValue(index, cp);
- break;
- case 'I':
- m = new IntegerMemberValue(index, cp);
- break;
- case 'J':
- m = new LongMemberValue(index, cp);
- break;
- case 'S':
- m = new ShortMemberValue(index, cp);
- break;
- case 'Z':
- m = new BooleanMemberValue(index, cp);
- break;
- case 's':
- m = new StringMemberValue(index, cp);
- break;
- default :
- throw new RuntimeException("unknown tag:" + tag);
- }
-
- memberValue = m;
- super.constValueMember(tag, index);
- }
-
- void enumMemberValue(int typeNameIndex, int constNameIndex)
- throws Exception
- {
- memberValue = new EnumMemberValue(typeNameIndex,
- constNameIndex, pool);
- super.enumMemberValue(typeNameIndex, constNameIndex);
- }
-
- void classMemberValue(int index) throws Exception
- {
- memberValue = new ClassMemberValue(index, pool);
- super.classMemberValue(index);
- }
-
- int annotationMemberValue(int pos) throws Exception
- {
- Annotation anno = currentAnno;
- pos = super.annotationMemberValue(pos);
- memberValue = new AnnotationMemberValue(currentAnno, pool);
- currentAnno = anno;
- return pos;
- }
-
- int arrayMemberValue(int pos, int num) throws Exception
- {
- ArrayMemberValue amv = new ArrayMemberValue(pool);
- MemberValue[] elements = new MemberValue[num];
- for (int i = 0; i < num; ++i)
- {
- pos = memberValue(pos);
- elements[i] = memberValue;
- }
-
- amv.setValue(elements);
- memberValue = amv;
- return pos;
- }
- }
+public class AnnotationsAttribute extends AttributeInfo {
+ /**
+ * The name of the <code>RuntimeVisibleAnnotations</code> attribute.
+ */
+ public static final String visibleTag = "RuntimeVisibleAnnotations";
+
+ /**
+ * The name of the <code>RuntimeInvisibleAnnotations</code> attribute.
+ */
+ public static final String invisibleTag = "RuntimeInvisibleAnnotations";
+
+ /**
+ * Constructs a <code>Runtime(In)VisisbleAnnotations_attribute</code>.
+ *
+ * @param cp constant pool
+ * @param attrname attribute name (<code>visibleTag</code> or
+ * <code>invisibleTag</code>).
+ * @param info the contents of this attribute. It does not
+ * include <code>attribute_name_index</code> or
+ * <code>attribute_length</code>.
+ */
+ public AnnotationsAttribute(ConstPool cp, String attrname, byte[] info) {
+ super(cp, attrname, info);
+ }
+
+ /**
+ * Constructs an empty
+ * <code>Runtime(In)VisisbleAnnotations_attribute</code>.
+ * A new annotation can be later added to the created attribute
+ * by <code>setAnnotations()</code>.
+ *
+ * @param cp constant pool
+ * @param attrname attribute name (<code>visibleTag</code> or
+ * <code>invisibleTag</code>).
+ * @see #setAnnotations(Annotation[])
+ */
+ public AnnotationsAttribute(ConstPool cp, String attrname) {
+ this(cp, attrname, new byte[] { 0, 0 });
+ }
+
+ /**
+ * @param n the attribute name.
+ */
+ AnnotationsAttribute(ConstPool cp, int n, DataInputStream in)
+ throws IOException
+ {
+ super(cp, n, in);
+ }
+
+ /**
+ * Returns <code>num_annotations</code>.
+ */
+ public int numAnnotations() {
+ return ByteArray.readU16bit(info, 0);
+ }
+
+ /**
+ * Copies this attribute and returns a new copy.
+ */
+ public AttributeInfo copy(ConstPool newCp, Map classnames) {
+ Copier copier = new Copier(info, constPool, newCp, classnames);
+ try {
+ copier.annotationArray();
+ return new AnnotationsAttribute(newCp, getName(), copier.close());
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
+
+ /**
+ * Parses the annotations and returns a data structure representing
+ * the annotation with the specified type. See also
+ * <code>getAnnotations()</code> as to the returned data structure.
+ *
+ * @param type the annotation type.
+ * @see #getAnnotations()
+ */
+ public Annotation getAnnotation(String type) {
+ Annotation[] annotations = getAnnotations();
+ for (int i = 0; i < annotations.length; i++) {
+ if (annotations[i].getTypeName().equals(type))
+ return annotations[i];
+ }
+
+ return null;
+ }
+
+ /**
+ * Adds an annotation. If there is an annotation with the same type,
+ * it is removed before the new annotation is added.
+ *
+ * @param annotation the added annotation.
+ */
+ public void addAnnotation(Annotation annotation) {
+ String type = annotation.getTypeName();
+ Annotation[] annotations = getAnnotations();
+ for (int i = 0; i < annotations.length; i++) {
+ if (annotations[i].getTypeName().equals(type)) {
+ annotations[i] = annotation;
+ setAnnotations(annotations);
+ return;
+ }
+ }
+
+ Annotation[] newlist = new Annotation[annotations.length + 1];
+ System.arraycopy(annotations, 0, newlist, 0, annotations.length);
+ newlist[annotations.length] = annotation;
+ setAnnotations(newlist);
+ }
+
+ /**
+ * Parses the annotations and returns a data structure representing
+ * that parsed annotations. Note that changes of the node values of the
+ * returned tree are not reflected on the annotations represented by
+ * this object unless the tree is copied back to this object by
+ * <code>setAnnotations()</code>.
+ *
+ * @see #setAnnotations(Annotation[])
+ */
+ public Annotation[] getAnnotations() {
+ try {
+ return new Parser(info, constPool).parseAnnotations();
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
+
+ /**
+ * Changes the annotations represented by this object according to
+ * the given array of <code>Annotation</code> objects.
+ *
+ * @param annotations the data structure representing the
+ * new annotations.
+ */
+ public void setAnnotations(Annotation[] annotations) {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ AnnotationsWriter writer = new AnnotationsWriter(output, constPool);
+ try {
+ int n = annotations.length;
+ writer.numAnnotations(n);
+ for (int i = 0; i < n; ++i)
+ annotations[i].write(writer);
+
+ writer.close();
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e); // should never reach here.
+ }
+
+ set(output.toByteArray());
+ }
+
+ /**
+ * Changes the annotations. A call to this method is equivalent to:
+ * <ul><pre>setAnnotations(new Annotation[] { annotation })</pre></ul>
+ *
+ * @param annotation the data structure representing
+ * the new annotation.
+ */
+ public void setAnnotation(Annotation annotation) {
+ setAnnotations(new Annotation[] { annotation });
+ }
+
+ /**
+ * Returns a string representation of this object.
+ */
+ public String toString() {
+ Annotation[] a = getAnnotations();
+ StringBuffer sbuf = new StringBuffer();
+ int i = 0;
+ while (i < a.length) {
+ sbuf.append(a[i++].toString());
+ if (i != a.length)
+ sbuf.append(", ");
+ }
+
+ return sbuf.toString();
+ }
+
+ static class Walker {
+ byte[] info;
+
+ Walker(byte[] attrInfo) {
+ info = attrInfo;
+ }
+
+ final void parameters() throws Exception {
+ int numParam = info[0] & 0xff;
+ parameters(numParam, 1);
+ }
+
+ void parameters(int numParam, int pos) throws Exception {
+ for (int i = 0; i < numParam; ++i)
+ pos = annotationArray(pos);
+ }
+
+ final void annotationArray() throws Exception {
+ annotationArray(0);
+ }
+
+ final int annotationArray(int pos) throws Exception {
+ int num = ByteArray.readU16bit(info, pos);
+ return annotationArray(pos + 2, num);
+ }
+
+ int annotationArray(int pos, int num) throws Exception {
+ for (int i = 0; i < num; ++i)
+ pos = annotation(pos);
+
+ return pos;
+ }
+
+ final int annotation(int pos) throws Exception {
+ int type = ByteArray.readU16bit(info, pos);
+ int numPairs = ByteArray.readU16bit(info, pos + 2);
+ return annotation(pos + 4, type, numPairs);
+ }
+
+ int annotation(int pos, int type, int numPairs) throws Exception {
+ for (int j = 0; j < numPairs; ++j)
+ pos = memberValuePair(pos);
+
+ return pos;
+ }
+
+ final int memberValuePair(int pos) throws Exception {
+ int nameIndex = ByteArray.readU16bit(info, pos);
+ return memberValuePair(pos + 2, nameIndex);
+ }
+
+ int memberValuePair(int pos, int nameIndex) throws Exception {
+ return memberValue(pos);
+ }
+
+ final int memberValue(int pos) throws Exception {
+ int tag = info[pos] & 0xff;
+ if (tag == 'e') {
+ int typeNameIndex = ByteArray.readU16bit(info, pos + 1);
+ int constNameIndex = ByteArray.readU16bit(info, pos + 3);
+ enumMemberValue(typeNameIndex, constNameIndex);
+ return pos + 5;
+ }
+ else if (tag == 'c') {
+ int index = ByteArray.readU16bit(info, pos + 1);
+ classMemberValue(index);
+ return pos + 3;
+ }
+ else if (tag == '@')
+ return annotationMemberValue(pos + 1);
+ else if (tag == '[') {
+ int num = ByteArray.readU16bit(info, pos + 1);
+ return arrayMemberValue(pos + 3, num);
+ }
+ else { // primitive types or String.
+ int index = ByteArray.readU16bit(info, pos + 1);
+ constValueMember(tag, index);
+ return pos + 3;
+ }
+ }
+
+ void constValueMember(int tag, int index) throws Exception {}
+
+ void enumMemberValue(int typeNameIndex, int constNameIndex)
+ throws Exception {
+ }
+
+ void classMemberValue(int index) throws Exception {}
+
+ int annotationMemberValue(int pos) throws Exception {
+ return annotation(pos);
+ }
+
+ int arrayMemberValue(int pos, int num) throws Exception {
+ for (int i = 0; i < num; ++i) {
+ pos = memberValue(pos);
+ }
+
+ return pos;
+ }
+ }
+
+ static class Copier extends Walker {
+ ByteArrayOutputStream output;
+ AnnotationsWriter writer;
+ ConstPool srcPool, destPool;
+ Map classnames;
+
+ /**
+ * Constructs a copier. This copier renames some class names
+ * into the new names specified by <code>map</code> when it copies
+ * an annotation attribute.
+ *
+ * @param info the source attribute.
+ * @param src the constant pool of the source class.
+ * @param dest the constant pool of the destination class.
+ * @param map pairs of replaced and substituted class names.
+ * It can be null.
+ */
+ Copier(byte[] info, ConstPool src, ConstPool dest, Map map) {
+ super(info);
+ output = new ByteArrayOutputStream();
+ writer = new AnnotationsWriter(output, dest);
+ srcPool = src;
+ destPool = dest;
+ classnames = map;
+ }
+
+ byte[] close() throws IOException {
+ writer.close();
+ return output.toByteArray();
+ }
+
+ void parameters(int numParam, int pos) throws Exception {
+ writer.numParameters(numParam);
+ super.parameters(numParam, pos);
+ }
+
+ int annotationArray(int pos, int num) throws Exception {
+ writer.numAnnotations(num);
+ return super.annotationArray(pos, num);
+ }
+
+ int annotation(int pos, int type, int numPairs) throws Exception {
+ writer.annotation(copy(type), numPairs);
+ return super.annotation(pos, type, numPairs);
+ }
+
+ int memberValuePair(int pos, int nameIndex) throws Exception {
+ writer.memberValuePair(copy(nameIndex));
+ return super.memberValuePair(pos, nameIndex);
+ }
+
+ void constValueMember(int tag, int index) throws Exception {
+ writer.constValueIndex(tag, copy(index));
+ super.constValueMember(tag, index);
+ }
+
+ void enumMemberValue(int typeNameIndex, int constNameIndex)
+ throws Exception
+ {
+ writer.enumConstValue(copy(typeNameIndex), copy(constNameIndex));
+ super.enumMemberValue(typeNameIndex, constNameIndex);
+ }
+
+ void classMemberValue(int index) throws Exception {
+ writer.classInfoIndex(copy(index));
+ super.classMemberValue(index);
+ }
+
+ int annotationMemberValue(int pos) throws Exception {
+ writer.annotationValue();
+ return super.annotationMemberValue(pos);
+ }
+
+ int arrayMemberValue(int pos, int num) throws Exception {
+ writer.arrayValue(num);
+ return super.arrayMemberValue(pos, num);
+ }
+
+ /**
+ * Copies a constant pool entry into the destination constant pool
+ * and returns the index of the copied entry.
+ *
+ * @param srcIndex the index of the copied entry into the source
+ * constant pool.
+ * @return the index of the copied item into the destination
+ * constant pool.
+ */
+ int copy(int srcIndex) {
+ return srcPool.copy(srcIndex, destPool, classnames);
+ }
+ }
+
+ static class Parser extends Walker {
+ ConstPool pool;
+ Annotation[][] allParams; // all parameters
+ Annotation[] allAnno; // all annotations
+ Annotation currentAnno; // current annotation
+ MemberValue memberValue;
+
+ /**
+ * Constructs a parser. This parser constructs a parse tree of
+ * the annotations.
+ *
+ * @param info the attribute.
+ * @param src the constant pool.
+ */
+ Parser(byte[] info, ConstPool cp) {
+ super(info);
+ pool = cp;
+ }
+
+ Annotation[][] parseParameters() throws Exception {
+ parameters();
+ return allParams;
+ }
+
+ Annotation[] parseAnnotations() throws Exception {
+ annotationArray();
+ return allAnno;
+ }
+
+ void parameters(int numParam, int pos) throws Exception {
+ Annotation[][] params = new Annotation[numParam][];
+ for (int i = 0; i < numParam; ++i) {
+ pos = annotationArray(pos);
+ params[i] = allAnno;
+ }
+
+ allParams = params;
+ }
+
+ int annotationArray(int pos, int num) throws Exception {
+ Annotation[] array = new Annotation[num];
+ for (int i = 0; i < num; ++i) {
+ pos = annotation(pos);
+ array[i] = currentAnno;
+ }
+
+ allAnno = array;
+ return pos;
+ }
+
+ int annotation(int pos, int type, int numPairs) throws Exception {
+ currentAnno = new Annotation(type, pool);
+ return super.annotation(pos, type, numPairs);
+ }
+
+ int memberValuePair(int pos, int nameIndex) throws Exception {
+ pos = super.memberValuePair(pos, nameIndex);
+ currentAnno.addMemberValue(nameIndex, memberValue);
+ return pos;
+ }
+
+ void constValueMember(int tag, int index) throws Exception {
+ MemberValue m;
+ ConstPool cp = pool;
+ switch (tag) {
+ case 'B' :
+ m = new ByteMemberValue(index, cp);
+ break;
+ case 'C' :
+ m = new CharMemberValue(index, cp);
+ break;
+ case 'D' :
+ m = new DoubleMemberValue(index, cp);
+ break;
+ case 'F' :
+ m = new FloatMemberValue(index, cp);
+ break;
+ case 'I' :
+ m = new IntegerMemberValue(index, cp);
+ break;
+ case 'J' :
+ m = new LongMemberValue(index, cp);
+ break;
+ case 'S' :
+ m = new ShortMemberValue(index, cp);
+ break;
+ case 'Z' :
+ m = new BooleanMemberValue(index, cp);
+ break;
+ case 's' :
+ m = new StringMemberValue(index, cp);
+ break;
+ default :
+ throw new RuntimeException("unknown tag:" + tag);
+ }
+
+ memberValue = m;
+ super.constValueMember(tag, index);
+ }
+
+ void enumMemberValue(int typeNameIndex, int constNameIndex)
+ throws Exception
+ {
+ memberValue = new EnumMemberValue(typeNameIndex,
+ constNameIndex, pool);
+ super.enumMemberValue(typeNameIndex, constNameIndex);
+ }
+
+ void classMemberValue(int index) throws Exception {
+ memberValue = new ClassMemberValue(index, pool);
+ super.classMemberValue(index);
+ }
+
+ int annotationMemberValue(int pos) throws Exception {
+ Annotation anno = currentAnno;
+ pos = super.annotationMemberValue(pos);
+ memberValue = new AnnotationMemberValue(currentAnno, pool);
+ currentAnno = anno;
+ return pos;
+ }
+
+ int arrayMemberValue(int pos, int num) throws Exception {
+ ArrayMemberValue amv = new ArrayMemberValue(pool);
+ MemberValue[] elements = new MemberValue[num];
+ for (int i = 0; i < num; ++i) {
+ pos = memberValue(pos);
+ elements[i] = memberValue;
+ }
+
+ amv.setValue(elements);
+ memberValue = amv;
+ return pos;
+ }
+ }
}
* @see javassist.CtMethod#getMethodInfo()
* @see javassist.CtConstructor#getMethodInfo()
*/
-public final class MethodInfo
-{
- ConstPool constPool;
- int accessFlags;
- int name;
- int descriptor;
- LinkedList attribute; // may be null
-
- /**
- * The name of constructors: <code><init></code>.
- */
- public static final String nameInit = "<init>";
-
- /**
- * The name of class initializer (static initializer):
- * <code><clinit></code>.
- */
- public static final String nameClinit = "<clinit>";
-
- private MethodInfo(ConstPool cp)
- {
- constPool = cp;
- attribute = null;
- }
-
- /**
- * Constructs a <code>method_info</code> structure.
- * The initial value of <code>access_flags</code> is zero.
- *
- * @param cp a constant pool table
- * @param methodname method name
- * @param desc method descriptor
- * @see Descriptor
- */
- public MethodInfo(ConstPool cp, String methodname, String desc)
- {
- this(cp);
- accessFlags = 0;
- name = cp.addUtf8Info(methodname);
- descriptor = constPool.addUtf8Info(desc);
- }
-
- MethodInfo(ConstPool cp, DataInputStream in) throws IOException
- {
- this(cp);
- read(in);
- }
-
- /**
- * Constructs a copy of <code>method_info</code> structure.
- * Class names appearing in the source <code>method_info</code>
- * are renamed according to <code>classnameMap</code>.
- * <p/>
- * <p>Note: only <code>Code</code> and <code>Exceptions</code>
- * attributes are copied from the source. The other attributes
- * are ignored.
- *
- * @param cp a constant pool table
- * @param methodname a method name
- * @param src a source <code>method_info</code>
- * @param classnameMap specifies pairs of replaced and substituted
- * name.
- * @see Descriptor
- */
- public MethodInfo(ConstPool cp, String methodname, MethodInfo src,
- Map classnameMap) throws BadBytecode
- {
- this(cp);
- read(src, methodname, classnameMap);
- }
-
- void prune(ConstPool cp)
- {
- new Exception("****** pruned *****").printStackTrace();
- attribute = null;
- name = cp.addUtf8Info(getName());
- descriptor = cp.addUtf8Info(getDescriptor());
- constPool = cp;
- }
-
- /**
- * Returns a method name.
- */
- public String getName()
- {
- return constPool.getUtf8Info(name);
- }
-
- /**
- * Sets a method name.
- */
- public void setName(String newName)
- {
- name = constPool.addUtf8Info(newName);
- }
-
- /**
- * Returns true if this is not a constructor or a class initializer
- * (static initializer).
- */
- public boolean isMethod()
- {
- String n = getName();
- return !n.equals(nameInit) && !n.equals(nameClinit);
- }
-
- /**
- * Returns a constant pool table used by this method.
- */
- public ConstPool getConstPool()
- {
- return constPool;
- }
-
- /**
- * Returns true if this is a constructor.
- */
- public boolean isConstructor()
- {
- return getName().equals(nameInit);
- }
-
- /**
- * Returns true if this is a class initializer (static initializer).
- */
- public boolean isStaticInitializer()
- {
- return getName().equals(nameClinit);
- }
-
- /**
- * Returns access flags.
- *
- * @see AccessFlag
- */
- public int getAccessFlags()
- {
- return accessFlags;
- }
-
- /**
- * Sets access flags.
- *
- * @see AccessFlag
- */
- public void setAccessFlags(int acc)
- {
- accessFlags = acc;
- }
-
- /**
- * Returns a method descriptor.
- *
- * @see Descriptor
- */
- public String getDescriptor()
- {
- return constPool.getUtf8Info(descriptor);
- }
-
- /**
- * Sets a method descriptor.
- *
- * @see Descriptor
- */
- public void setDescriptor(String desc)
- {
- if (!desc.equals(getDescriptor()))
- descriptor = constPool.addUtf8Info(desc);
- }
-
- /**
- * Returns all the attributes.
- * A new element can be added to the returned list
- * and an existing element can be removed from the list.
- *
- * @return a list of <code>AttributeInfo</code> objects.
- * @see AttributeInfo
- */
- public List getAttributes()
- {
- if (attribute == null)
- attribute = new LinkedList();
-
- return attribute;
- }
-
- /**
- * Returns the attribute with the specified name.
- * If it is not found, this method returns null.
- *
- * @param name attribute name
- * @return an <code>AttributeInfo</code> object or null.
- */
- public AttributeInfo getAttribute(String name)
- {
- return AttributeInfo.lookup(attribute, name);
- }
-
- /**
- * Appends an attribute. If there is already an attribute with
- * the same name, the new one substitutes for it.
- */
- public void addAttribute(AttributeInfo info)
- {
- if (attribute == null)
- attribute = new LinkedList();
-
- AttributeInfo.remove(attribute, info.getName());
- attribute.add(info);
- }
-
- /**
- * Returns an Exceptions attribute.
- *
- * @return an Exceptions attribute
- * or null if it is not specified.
- */
- public ExceptionsAttribute getExceptionsAttribute()
- {
- AttributeInfo info
- = AttributeInfo.lookup(attribute, ExceptionsAttribute.tag);
- return (ExceptionsAttribute) info;
- }
-
- /**
- * Returns a Code attribute.
- *
- * @return a Code attribute
- * or null if it is not specified.
- */
- public CodeAttribute getCodeAttribute()
- {
- AttributeInfo info
- = AttributeInfo.lookup(attribute, CodeAttribute.tag);
- return (CodeAttribute) info;
- }
-
- /**
- * Removes an Exception attribute.
- */
- public void removeExceptionsAttribute()
- {
- AttributeInfo.remove(attribute, ExceptionsAttribute.tag);
- }
-
- /**
- * Adds an Exception attribute.
- * <p/>
- * <p>The added attribute must share the same constant pool table
- * as this <code>method_info</code> structure.
- */
- public void setExceptionsAttribute(ExceptionsAttribute cattr)
- {
- removeExceptionsAttribute();
- if (attribute == null)
- attribute = new LinkedList();
-
- attribute.add(cattr);
- }
-
- /**
- * Removes a Code attribute.
- */
- public void removeCodeAttribute()
- {
- AttributeInfo.remove(attribute, CodeAttribute.tag);
- }
-
- /**
- * Adds a Code attribute.
- * <p/>
- * <p>The added attribute must share the same constant pool table
- * as this <code>method_info</code> structure.
- */
- public void setCodeAttribute(CodeAttribute cattr)
- {
- removeCodeAttribute();
- if (attribute == null)
- attribute = new LinkedList();
-
- attribute.add(cattr);
- }
-
- /**
- * Returns the line number of the source line corresponding to the
- * specified bytecode contained in this method.
- *
- * @param pos the position of the bytecode (>= 0).
- * an index into the code array.
- * @return -1 if this information is not available.
- */
- public int getLineNumber(int pos)
- {
- CodeAttribute ca = getCodeAttribute();
- if (ca == null)
- return -1;
-
- LineNumberAttribute ainfo =
- (LineNumberAttribute) ca.getAttribute(LineNumberAttribute.tag);
- if (ainfo == null)
- return -1;
-
- return ainfo.toLineNumber(pos);
- }
-
- /**
- * Changes a super constructor called by this constructor.
- * <p/>
- * <p>This method modifies a call to <code>super()</code>,
- * which should be at the
- * head of a constructor body, so that a constructor in a different
- * super class is called. This method does not change actural
- * parameters. Hence the new super class must have a constructor
- * with the same signature as the original one.
- * <p/>
- * <p>This method should be called when the super class
- * of the class declaring this method is changed.
- * <p/>
- * <p>This method does not perform anything unless this
- * <code>MethodInfo</code> represents a constructor.
- *
- * @param superclass the new super class
- */
- public void setSuperclass(String superclass) throws BadBytecode
- {
- if (!isConstructor())
- return;
-
- CodeAttribute ca = getCodeAttribute();
- byte[] code = ca.getCode();
- CodeIterator iterator = ca.iterator();
- int pos = iterator.skipSuperConstructor();
- if (pos >= 0)
- { // not this()
- ConstPool cp = constPool;
- int mref = ByteArray.readU16bit(code, pos + 1);
- int nt = cp.getMethodrefNameAndType(mref);
- int sc = cp.addClassInfo(superclass);
- int mref2 = cp.addMethodrefInfo(sc, nt);
- ByteArray.write16bit(mref2, code, pos + 1);
- }
- }
-
- private void read(MethodInfo src, String methodname, Map classnames)
- throws BadBytecode
- {
- ConstPool destCp = constPool;
- accessFlags = src.accessFlags;
- name = destCp.addUtf8Info(methodname);
-
- ConstPool srcCp = src.constPool;
- String desc = srcCp.getUtf8Info(src.descriptor);
- String desc2 = Descriptor.rename(desc, classnames);
- descriptor = destCp.addUtf8Info(desc2);
-
- attribute = new LinkedList();
- ExceptionsAttribute eattr = src.getExceptionsAttribute();
- if (eattr != null)
- attribute.add(eattr.copy(destCp, classnames));
-
- CodeAttribute cattr = src.getCodeAttribute();
- if (cattr != null)
- attribute.add(cattr.copy(destCp, classnames));
- }
-
- private void read(DataInputStream in) throws IOException
- {
- accessFlags = in.readUnsignedShort();
- name = in.readUnsignedShort();
- descriptor = in.readUnsignedShort();
- int n = in.readUnsignedShort();
- attribute = new LinkedList();
- for (int i = 0; i < n; ++i)
- attribute.add(AttributeInfo.read(constPool, in));
- }
-
- void write(DataOutputStream out) throws IOException
- {
- out.writeShort(accessFlags);
- out.writeShort(name);
- out.writeShort(descriptor);
-
- if (attribute == null)
- out.writeShort(0);
- else
- {
- out.writeShort(attribute.size());
- AttributeInfo.writeAll(attribute, out);
- }
- }
+public final class MethodInfo {
+ ConstPool constPool;
+ int accessFlags;
+ int name;
+ int descriptor;
+ LinkedList attribute; // may be null
+
+ /**
+ * The name of constructors: <code><init></code>.
+ */
+ public static final String nameInit = "<init>";
+
+ /**
+ * The name of class initializer (static initializer):
+ * <code><clinit></code>.
+ */
+ public static final String nameClinit = "<clinit>";
+
+ private MethodInfo(ConstPool cp) {
+ constPool = cp;
+ attribute = null;
+ }
+
+ /**
+ * Constructs a <code>method_info</code> structure.
+ * The initial value of <code>access_flags</code> is zero.
+ *
+ * @param cp a constant pool table
+ * @param methodname method name
+ * @param desc method descriptor
+ * @see Descriptor
+ */
+ public MethodInfo(ConstPool cp, String methodname, String desc) {
+ this(cp);
+ accessFlags = 0;
+ name = cp.addUtf8Info(methodname);
+ descriptor = constPool.addUtf8Info(desc);
+ }
+
+ MethodInfo(ConstPool cp, DataInputStream in) throws IOException {
+ this(cp);
+ read(in);
+ }
+
+ /**
+ * Constructs a copy of <code>method_info</code> structure.
+ * Class names appearing in the source <code>method_info</code>
+ * are renamed according to <code>classnameMap</code>.
+ *
+ * <p>Note: only <code>Code</code> and <code>Exceptions</code>
+ * attributes are copied from the source. The other attributes
+ * are ignored.
+ *
+ * @param cp a constant pool table
+ * @param methodname a method name
+ * @param src a source <code>method_info</code>
+ * @param classnameMap specifies pairs of replaced and substituted
+ * name.
+ * @see Descriptor
+ */
+ public MethodInfo(ConstPool cp, String methodname, MethodInfo src,
+ Map classnameMap) throws BadBytecode
+ {
+ this(cp);
+ read(src, methodname, classnameMap);
+ }
+
+ void prune(ConstPool cp) {
+ attribute = null;
+ name = cp.addUtf8Info(getName());
+ descriptor = cp.addUtf8Info(getDescriptor());
+ constPool = cp;
+ }
+
+ /**
+ * Returns a method name.
+ */
+ public String getName() {
+ return constPool.getUtf8Info(name);
+ }
+
+ /**
+ * Sets a method name.
+ */
+ public void setName(String newName) {
+ name = constPool.addUtf8Info(newName);
+ }
+
+ /**
+ * Returns true if this is not a constructor or a class initializer
+ * (static initializer).
+ */
+ public boolean isMethod() {
+ String n = getName();
+ return !n.equals(nameInit) && !n.equals(nameClinit);
+ }
+
+ /**
+ * Returns a constant pool table used by this method.
+ */
+ public ConstPool getConstPool() {
+ return constPool;
+ }
+
+ /**
+ * Returns true if this is a constructor.
+ */
+ public boolean isConstructor() {
+ return getName().equals(nameInit);
+ }
+
+ /**
+ * Returns true if this is a class initializer (static initializer).
+ */
+ public boolean isStaticInitializer() {
+ return getName().equals(nameClinit);
+ }
+
+ /**
+ * Returns access flags.
+ *
+ * @see AccessFlag
+ */
+ public int getAccessFlags() {
+ return accessFlags;
+ }
+
+ /**
+ * Sets access flags.
+ *
+ * @see AccessFlag
+ */
+ public void setAccessFlags(int acc) {
+ accessFlags = acc;
+ }
+
+ /**
+ * Returns a method descriptor.
+ *
+ * @see Descriptor
+ */
+ public String getDescriptor() {
+ return constPool.getUtf8Info(descriptor);
+ }
+
+ /**
+ * Sets a method descriptor.
+ *
+ * @see Descriptor
+ */
+ public void setDescriptor(String desc) {
+ if (!desc.equals(getDescriptor()))
+ descriptor = constPool.addUtf8Info(desc);
+ }
+
+ /**
+ * Returns all the attributes.
+ * A new element can be added to the returned list
+ * and an existing element can be removed from the list.
+ *
+ * @return a list of <code>AttributeInfo</code> objects.
+ * @see AttributeInfo
+ */
+ public List getAttributes() {
+ if (attribute == null)
+ attribute = new LinkedList();
+
+ return attribute;
+ }
+
+ /**
+ * Returns the attribute with the specified name.
+ * If it is not found, this method returns null.
+ *
+ * @param name attribute name
+ * @return an <code>AttributeInfo</code> object or null.
+ */
+ public AttributeInfo getAttribute(String name) {
+ return AttributeInfo.lookup(attribute, name);
+ }
+
+ /**
+ * Appends an attribute. If there is already an attribute with
+ * the same name, the new one substitutes for it.
+ */
+ public void addAttribute(AttributeInfo info) {
+ if (attribute == null)
+ attribute = new LinkedList();
+
+ AttributeInfo.remove(attribute, info.getName());
+ attribute.add(info);
+ }
+
+ /**
+ * Returns an Exceptions attribute.
+ *
+ * @return an Exceptions attribute
+ * or null if it is not specified.
+ */
+ public ExceptionsAttribute getExceptionsAttribute() {
+ AttributeInfo info
+ = AttributeInfo.lookup(attribute, ExceptionsAttribute.tag);
+ return (ExceptionsAttribute)info;
+ }
+
+ /**
+ * Returns a Code attribute.
+ *
+ * @return a Code attribute
+ * or null if it is not specified.
+ */
+ public CodeAttribute getCodeAttribute() {
+ AttributeInfo info
+ = AttributeInfo.lookup(attribute, CodeAttribute.tag);
+ return (CodeAttribute)info;
+ }
+
+ /**
+ * Removes an Exception attribute.
+ */
+ public void removeExceptionsAttribute() {
+ AttributeInfo.remove(attribute, ExceptionsAttribute.tag);
+ }
+
+ /**
+ * Adds an Exception attribute.
+ *
+ * <p>The added attribute must share the same constant pool table
+ * as this <code>method_info</code> structure.
+ */
+ public void setExceptionsAttribute(ExceptionsAttribute cattr) {
+ removeExceptionsAttribute();
+ if (attribute == null)
+ attribute = new LinkedList();
+
+ attribute.add(cattr);
+ }
+
+ /**
+ * Removes a Code attribute.
+ */
+ public void removeCodeAttribute() {
+ AttributeInfo.remove(attribute, CodeAttribute.tag);
+ }
+
+ /**
+ * Adds a Code attribute.
+ *
+ * <p>The added attribute must share the same constant pool table
+ * as this <code>method_info</code> structure.
+ */
+ public void setCodeAttribute(CodeAttribute cattr) {
+ removeCodeAttribute();
+ if (attribute == null)
+ attribute = new LinkedList();
+
+ attribute.add(cattr);
+ }
+
+ /**
+ * Returns the line number of the source line corresponding to the
+ * specified bytecode contained in this method.
+ *
+ * @param pos the position of the bytecode (>= 0).
+ * an index into the code array.
+ * @return -1 if this information is not available.
+ */
+ public int getLineNumber(int pos) {
+ CodeAttribute ca = getCodeAttribute();
+ if (ca == null)
+ return -1;
+
+ LineNumberAttribute ainfo =
+ (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag);
+ if (ainfo == null)
+ return -1;
+
+ return ainfo.toLineNumber(pos);
+ }
+
+ /**
+ * Changes a super constructor called by this constructor.
+ *
+ * <p>This method modifies a call to <code>super()</code>,
+ * which should be at the
+ * head of a constructor body, so that a constructor in a different
+ * super class is called. This method does not change actural
+ * parameters. Hence the new super class must have a constructor
+ * with the same signature as the original one.
+ *
+ * <p>This method should be called when the super class
+ * of the class declaring this method is changed.
+ *
+ * <p>This method does not perform anything unless this
+ * <code>MethodInfo</code> represents a constructor.
+ *
+ * @param superclass the new super class
+ */
+ public void setSuperclass(String superclass) throws BadBytecode {
+ if (!isConstructor())
+ return;
+
+ CodeAttribute ca = getCodeAttribute();
+ byte[] code = ca.getCode();
+ CodeIterator iterator = ca.iterator();
+ int pos = iterator.skipSuperConstructor();
+ if (pos >= 0) { // not this()
+ ConstPool cp = constPool;
+ int mref = ByteArray.readU16bit(code, pos + 1);
+ int nt = cp.getMethodrefNameAndType(mref);
+ int sc = cp.addClassInfo(superclass);
+ int mref2 = cp.addMethodrefInfo(sc, nt);
+ ByteArray.write16bit(mref2, code, pos + 1);
+ }
+ }
+
+ private void read(MethodInfo src, String methodname, Map classnames)
+ throws BadBytecode
+ {
+ ConstPool destCp = constPool;
+ accessFlags = src.accessFlags;
+ name = destCp.addUtf8Info(methodname);
+
+ ConstPool srcCp = src.constPool;
+ String desc = srcCp.getUtf8Info(src.descriptor);
+ String desc2 = Descriptor.rename(desc, classnames);
+ descriptor = destCp.addUtf8Info(desc2);
+
+ attribute = new LinkedList();
+ ExceptionsAttribute eattr = src.getExceptionsAttribute();
+ if (eattr != null)
+ attribute.add(eattr.copy(destCp, classnames));
+
+ CodeAttribute cattr = src.getCodeAttribute();
+ if (cattr != null)
+ attribute.add(cattr.copy(destCp, classnames));
+ }
+
+ private void read(DataInputStream in) throws IOException {
+ accessFlags = in.readUnsignedShort();
+ name = in.readUnsignedShort();
+ descriptor = in.readUnsignedShort();
+ int n = in.readUnsignedShort();
+ attribute = new LinkedList();
+ for (int i = 0; i < n; ++i)
+ attribute.add(AttributeInfo.read(constPool, in));
+ }
+
+ void write(DataOutputStream out) throws IOException {
+ out.writeShort(accessFlags);
+ out.writeShort(name);
+ out.writeShort(descriptor);
+
+ if (attribute == null)
+ out.writeShort(0);
+ else {
+ out.writeShort(attribute.size());
+ AttributeInfo.writeAll(attribute, out);
+ }
+ }
}
import javassist.bytecode.ConstPool;
import javassist.bytecode.Descriptor;
-
import java.io.IOException;
/**
* @author <a href="mailto:bill@jboss.org">Bill Burke</a>
* @author Shigeru Chiba
*/
-public class ClassMemberValue extends MemberValue
-{
- int valueIndex;
+public class ClassMemberValue extends MemberValue {
+ int valueIndex;
- /**
- * Constructs a string constant value. The initial value is specified
- * by the constant pool entry at the given index.
- *
- * @param index the index of a CONSTANT_Utf8_info structure.
- */
- public ClassMemberValue(int index, ConstPool cp)
- {
- super('c', cp);
- this.valueIndex = index;
- }
+ /**
+ * Constructs a string constant value. The initial value is specified
+ * by the constant pool entry at the given index.
+ *
+ * @param index the index of a CONSTANT_Utf8_info structure.
+ */
+ public ClassMemberValue(int index, ConstPool cp) {
+ super('c', cp);
+ this.valueIndex = index;
+ }
- /**
- * Constructs a string constant value.
- *
- * @param className the initial value.
- */
- public ClassMemberValue(String className, ConstPool cp)
- {
- super('c', cp);
- setValue(className);
- }
+ /**
+ * Constructs a string constant value.
+ *
+ * @param className the initial value.
+ */
+ public ClassMemberValue(String className, ConstPool cp) {
+ super('c', cp);
+ setValue(className);
+ }
- /**
- * Constructs a string constant value.
- * The initial value is java.lang.Class.
- */
- public ClassMemberValue(ConstPool cp)
- {
- super('c', cp);
- setValue("java.lang.Class");
- }
+ /**
+ * Constructs a string constant value.
+ * The initial value is java.lang.Class.
+ */
+ public ClassMemberValue(ConstPool cp) {
+ super('c', cp);
+ setValue("java.lang.Class");
+ }
- /**
- * Obtains the value of the member.
- *
- * @return fully-qualified class name.
- */
- public String getValue()
- {
- String v = cp.getUtf8Info(valueIndex);
- return Descriptor.toClassName(v);
- }
+ /**
+ * Obtains the value of the member.
+ *
+ * @return fully-qualified class name.
+ */
+ public String getValue() {
+ String v = cp.getUtf8Info(valueIndex);
+ return Descriptor.toClassName(v);
+ }
- /**
- * Sets the value of the member.
- *
- * @param newClassName fully-qualified class name.
- */
- public void setValue(String newClassName)
- {
- String setTo = Descriptor.of(newClassName);
- valueIndex = cp.addUtf8Info(setTo);
- }
+ /**
+ * Sets the value of the member.
+ *
+ * @param newClassName fully-qualified class name.
+ */
+ public void setValue(String newClassName) {
+ String setTo = Descriptor.of(newClassName);
+ valueIndex = cp.addUtf8Info(setTo);
+ }
- /**
- * Obtains the string representation of this object.
- */
- public String toString()
- {
- return "<" + getValue() + " class>";
- }
+ /**
+ * Obtains the string representation of this object.
+ */
+ public String toString() {
+ return "<" + getValue() + " class>";
+ }
- void write(AnnotationsWriter writer) throws IOException
- {
- writer.classInfoIndex(valueIndex);
- }
+ void write(AnnotationsWriter writer) throws IOException {
+ writer.classInfoIndex(valueIndex);
+ }
- /**
- * Accepts a visitor.
- */
- public void accept(MemberValueVisitor visitor)
- {
- visitor.visitClassMemberValue(this);
- }
+ /**
+ * Accepts a visitor.
+ */
+ public void accept(MemberValueVisitor visitor) {
+ visitor.visitClassMemberValue(this);
+ }
}