diff options
Diffstat (limited to 'src/main/javassist/ClassPool.java')
-rw-r--r-- | src/main/javassist/ClassPool.java | 497 |
1 files changed, 130 insertions, 367 deletions
diff --git a/src/main/javassist/ClassPool.java b/src/main/javassist/ClassPool.java index 137ad3a9..cd239522 100644 --- a/src/main/javassist/ClassPool.java +++ b/src/main/javassist/ClassPool.java @@ -83,10 +83,23 @@ import java.util.Hashtable; * @see javassist.ClassPath * @see javassist.Translator */ -public class ClassPool extends AbsClassPool { - protected AbsClassPool source; +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 Translator translator; protected Hashtable classes; // should be synchronous /** @@ -97,62 +110,24 @@ public class ClassPool extends AbsClassPool { /** * Creates a class pool. * - * @param src the source of class files. If it is null, - * the class search path is initially null. - * @param p the parent of this class pool. + * @param p 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 p) { - this(new ClassPoolTail(), p); - } - - ClassPool(AbsClassPool src, ClassPool parent) { + public ClassPool(ClassPool parent) { this.classes = new Hashtable(); - this.source = src; + this.source = new ClassPoolTail(); this.parent = parent; if (parent == null) { - // if this has no parent, it must include primitive types - // even if this.source is not a ClassPoolTail. CtClass[] pt = CtClass.primitiveTypes; for (int i = 0; i < pt.length; ++i) classes.put(pt[i].getName(), pt[i]); } - this.translator = null; this.cflow = null; } /** - * Adds a new translator at the end of the translator chain. - * - * @param trans a new translator associated with this class pool. - * @throws RuntimeException if trans.start() throws an exception. - */ - public void addTranslator(Translator trans) throws RuntimeException { - ClassPool cp; - if (translator == null) - cp = this; - else { - ClassPool s = this; - while (s.source instanceof ClassPool) - s = (ClassPool)s.source; - - cp = new ClassPool(s.source, parent); - s.source = cp; - } - - cp.translator = trans; - try { - trans.start(cp); - } - catch (Exception e) { - throw new RuntimeException( - "Translator.start() throws an exception: " - + e.toString()); - } - } - - /** * Returns the default class pool. * The returned object is always identical since this method is * a singleton factory. @@ -210,12 +185,12 @@ public class ClassPool extends AbsClassPool { classes.remove(classname); } - /** - * Returns the class search path. - */ - public String toString() { - return source.toString(); - } + /** + * Returns the class search path. + */ + public String toString() { + return source.toString(); + } /** * Records a name that never exists. @@ -232,14 +207,6 @@ public class ClassPool extends AbsClassPool { } /** - * Returns the <code>Translator</code> object associated with - * this <code>ClassPool</code>. - * - * @deprecated - */ - Translator getTranslator() { return translator; } - - /** * Records the <code>$cflow</code> variable for the field specified * by <code>cname</code> and <code>fname</code>. * @@ -267,133 +234,12 @@ public class ClassPool extends AbsClassPool { } /** - * Writes a class file specified with <code>classname</code> - * in the current directory. - * It never calls <code>onWrite()</code> on a translator. - * It is provided for debugging. - * - * @param classname the name of the class written on a local disk. - */ - public void debugWriteFile(String classname) - throws NotFoundException, CannotCompileException, IOException - { - debugWriteFile(classname, "."); - } - - /** - * Writes a class file specified with <code>classname</code>. - * It never calls <code>onWrite()</code> on a translator. - * It is provided for debugging. - * - * @param classname the name of the class written on a local disk. - * @param directoryName it must end without a directory separator. - */ - public void debugWriteFile(String classname, String directoryName) - throws NotFoundException, CannotCompileException, IOException - { - writeFile(classname, directoryName, false); - } - - /* void writeFile(CtClass) should not be defined since writeFile() - * may be called on the class pool that does not contain the given - * CtClass object. - */ - - /** - * Writes a class file specified with <code>classname</code> - * in the current directory. - * It calls <code>onWrite()</code> on a translator. - * - * @param classname the name of the class written on a local disk. - */ - public void writeFile(String classname) - throws NotFoundException, CannotCompileException, IOException - { - writeFile(classname, "."); - } - - /** - * Writes a class file specified with <code>classname</code> - * on a local disk. - * It calls <code>onWrite()</code> on a translator. - * - * @param classname the name of the class written on a local disk. - * @param directoryName it must end without a directory separator. - */ - public void writeFile(String classname, String directoryName) - throws NotFoundException, CannotCompileException, IOException - { - writeFile(classname, directoryName, true); - } - - private void writeFile(String classname, String directoryName, - boolean callback) - throws NotFoundException, CannotCompileException, IOException - { - 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))); - write(classname, out, callback); - 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(); - } - } - - /** - * A simple class loader used by <code>writeAsClass()</code> - * in <code>ClassPool</code>. + * A simple class loader used by <code>toClass()</code> + * in <code>CtClass</code>. * This class loader is provided for convenience. If you need more * complex functionality, you should write your own class loader. * - * @see ClassPool#writeAsClass(String) + * @see CtClass#forName(String) * @see CtClass#toClass() */ public static class SimpleLoader extends ClassLoader { @@ -413,64 +259,29 @@ public class ClassPool extends AbsClassPool { } }; - private static SimpleLoader classLoader = new SimpleLoader(); + private static SimpleLoader classLoader = null; /** * Returns a <code>java.lang.Class</code> object that has been loaded - * by <code>writeAsClass()</code>. Such an object cannot be + * by <code>loadClass()</code>. Such an object cannot be * obtained by <code>java.lang.Class.forName()</code> because it has * been loaded by an internal class loader of Javassist. - * - * @see #writeAsClass(String) - * @see javassist.CtClass#toClass() */ - public static Class forName(String name) throws ClassNotFoundException { + static Class forName(String name) throws ClassNotFoundException { + if (classLoader == null) + classLoader = new SimpleLoader(); + return classLoader.loadClass(name); } - /** - * Returns a <code>java.lang.Class</code> object. - * It calls <code>write()</code> to obtain a class file and then - * loads the obtained class file into the JVM. The returned - * <code>Class</code> object represents the loaded class. - * - * <p>This method is provided for convenience. If you need more - * complex functionality, you should write your own class loader. - * - * <p>To load a class file, this method uses an internal class loader, - * which is an instance of <code>ClassPool.SimpleLoader</code>. - * Thus, that class file is not loaded by the system class loader, - * which should have loaded this <code>ClassPool</code> class. - * The internal class loader - * loads only the classes explicitly specified by this method - * <code>writeAsClass()</code>. The other classes are loaded - * by the parent class loader (the sytem class loader) by delegation. - * - * <p>For example, - * - * <ul><pre>class Line { Point p1, p2; }</pre></ul> - * - * <p>If the class <code>Line</code> is loaded by the internal class - * loader and the class <code>Point</code> has not been loaded yet, - * then the class <code>Point</code> that the class <code>Line</code> - * refers to is loaded by the parent class loader. There is no - * chance of modifying the definition of <code>Point</code> with - * Javassist. - * - * <p>The internal class loader is shared among all the instances - * of <code>ClassPool</code>. - * - * @param classname a fully-qualified class name. - * - * @see #forName(String) - * @see javassist.CtClass#toClass() - * @see javassist.Loader - */ - public Class writeAsClass(String classname) + static Class loadClass(String classname, byte[] classfile) throws NotFoundException, IOException, CannotCompileException { + if (classLoader == null) + classLoader = new SimpleLoader(); + try { - return classLoader.loadClass(classname, write(classname)); + return classLoader.loadClass(classname, classfile); } catch (ClassFormatError e) { throw new CannotCompileException(e, classname); @@ -478,122 +289,6 @@ public class ClassPool extends AbsClassPool { } /** - * Returns a byte array representing the class file. - * It calls <code>onWrite()</code> on a translator. - * - * @param classname a fully-qualified class name. - */ - public byte[] write(String classname) - throws NotFoundException, IOException, CannotCompileException - { - ByteArrayOutputStream barray = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream(barray); - try { - write(classname, out, true); - } - finally { - out.close(); - } - - return barray.toByteArray(); - } - - /** - * Writes a class file specified by <code>classname</code> - * to a given output stream. - * It calls <code>onWrite()</code> on a translator. - * - * <p>This method does not close the output stream in the end. - * - * @param classname a fully-qualified class name. - * @param out an output stream - */ - public void write(String classname, DataOutputStream out) - throws NotFoundException, CannotCompileException, IOException - { - write(classname, out, true); - } - - private void write(String classname, DataOutputStream out, - boolean callback) - throws NotFoundException, CannotCompileException, IOException - { - if (!write0(classname, out, callback)) - throw new NotFoundException(classname); - } - - boolean write0(String classname, DataOutputStream out, boolean callback) - throws NotFoundException, CannotCompileException, IOException - { - CtClass clazz = (CtClass)getCached(classname); - if (callback && translator != null - && (clazz == null || !clazz.isFrozen())) { - translator.onWrite(this, classname); - // The CtClass object might be overwritten. - clazz = (CtClass)getCached(classname); - } - - if (clazz == null || !clazz.isModified()) { - if (clazz != null) - clazz.freeze(); - - return source.write0(classname, out, callback); - } - else { - clazz.toBytecode(out); - return true; - } - } - - /* for CtClassType.getClassFile2(). Don't delegate to the parent. - */ - byte[] readSource(String classname) - throws NotFoundException, IOException, CannotCompileException - { - return source.readSource(classname); - } - - /* - * Is invoked by CtClassType.setName(). 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); - classes.put(newName, clazz); - } - - /* - * 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 c; - if (parent != null) { - try { - c = parent.get0(classname); - } - catch (NotFoundException e) { // some error happens. - throw new RuntimeException(e.toString()); - } - - if (c != null) - throw new RuntimeException(classname - + " is in a parent ClassPool. Use the parent."); - } - - c = getCached(classname); - if (c != null && c.isFrozen()) - throw new RuntimeException(classname + - ": frozen class (cannot edit)"); - } - - /** * 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 @@ -614,21 +309,30 @@ public class ClassPool extends AbsClassPool { public CtClass getAndRename(String orgName, String newName) throws NotFoundException { - CtClass clazz = null; - if (parent != null) - clazz = parent.get1(orgName); - - if (clazz == null) - clazz = get1(orgName); - - if (clazz == null) - throw new NotFoundException(orgName); + 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); + classes.put(newName, clazz); + } + /** * Reads a class file from the source and returns a reference * to the <code>CtClass</code> @@ -646,7 +350,7 @@ public class ClassPool extends AbsClassPool { * @param classname a fully-qualified class name. */ public CtClass get(String classname) throws NotFoundException { - CtClass clazz = get0(classname); + CtClass clazz = get0(classname, true); if (clazz == null) throw new NotFoundException(classname); else @@ -654,33 +358,51 @@ public class ClassPool extends AbsClassPool { } /** + * @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. */ - private synchronized CtClass get0(String classname) + protected synchronized CtClass get0(String classname, boolean useCache) throws NotFoundException { - CtClass clazz; - clazz = getCached(classname); - if (clazz != null) return clazz; - if (parent != null) { - clazz = parent.get0(classname); + CtClass clazz = null; + if (useCache) { + clazz = getCached(classname); if (clazz != null) return clazz; } - if (clazz == null) { - clazz = get1(classname); + if (!childFirstLookup && parent != null) { + clazz = parent.get0(classname, useCache); if (clazz != null) + return clazz; + } + + clazz = createCtClass(classname, useCache); + if (clazz != null) { + if (useCache) classes.put(classname, clazz); + + return clazz; } + if (childFirstLookup && parent != null) + clazz = parent.get0(classname, useCache); + return clazz; } - protected CtClass get1(String classname) throws NotFoundException { + /** + * 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. + */ + private CtClass createCtClass(String classname, boolean useCache) { if (classname.endsWith("[]")) { String base = classname.substring(0, classname.indexOf('[')); - if (getCached(base) == null && find(base) == null) + if ((!useCache || getCached(base) == null) && find(base) == null) return null; else return new CtArray(classname, this); @@ -693,16 +415,57 @@ public class ClassPool extends AbsClassPool { } /** - * Obtains the URL of the class file specified by classname. - * This method does not delegate to the parent pool. + * 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 CtClassType#getURL() */ 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."); + } + } + else + if (clazz.isFrozen()) + throw new RuntimeException(classname + + ": frozen class (cannot edit)"); + } + + /* for CtClassType.getClassFile2(). Don't delegate to the parent. + */ + byte[] readSource(String classname) + throws NotFoundException, IOException, CannotCompileException + { + return source.readSource(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> |