/**
* InstantJ
*
* Copyright (C) 2002 Andy Thomas
* Additional changes (C) 2002 Nils Meier
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*/
package instantj.compile;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* (C) 2002 Andy Thomas
* This class represents a single compilation source files class instance.
* This includes the classes name and the bytes that form the class.
*/
public class CompiledClass implements Serializable {
/** the compiled bytecode */
private byte[] bytecode;
/** the name of the class */
private String name;
/** inner classes */
private Map inners = null;
/** dependencies to other classes */
private Map dependencies = null;
/** cached mapping classloader2type */
private transient Map cl2type = null;
/**
* Constructor
*/
public CompiledClass(String name, byte[] bytecode) {
this.bytecode = bytecode;
this.name = name;
}
/**
* Returns the code.
* @return byte[]
*/
public byte[] getByteCode() {
return bytecode;
}
/**
* Returns the name.
* @return String
*/
public String getName() {
return name;
}
/**
* Returns a (new) java type for this compiled class. The classloader
* used wraps
* <il>
* <li>Thread.currentThread().getContextClassLoader() if not null
* <li>getClass().getClassLoader() otherwise
* </il>
* and will return the same type for that classloader on successive calls.
*/
public Class getType() {
// nothing cached yet?
if (cl2type==null) {
cl2type = new HashMap();
}
// lookup ClassLoader
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl==null) cl = getClass().getClassLoader();
// known?
Class result = (Class)cl2type.get(cl);
if (result==null) {
result = new CL(cl).loadClass();
cl2type.put(cl, result);
}
// done
return result;
}
/**
* Method addInnerClasses.
* @param inners
*/
public void addInnerClasses(Collection add) {
// check argument
if (add==null||add.size()==0) return;
// check state
if (inners==null) inners = new HashMap();
// loop through list
Iterator it = add.iterator();
while (it.hasNext()) {
CompiledClass cc = (CompiledClass)it.next();
inners.put(cc.getName(), cc);
}
// done
}
/**
* Add dependencies (other CompiledClasses)
*/
public void addDependencies(Collection add) {
// check argument
if (add==null||add.size()==0) return;
// check state
if (dependencies==null) dependencies = new HashMap();
// loop through list
Iterator it = add.iterator();
while (it.hasNext()) {
CompiledClass cc = (CompiledClass)it.next();
dependencies.put(cc.getName(), cc);
}
// done
}
/**
* Builds a map of compiled classes
*/
/*package*/ static Map map(Map map, Collection ccs) {
// check for null
if (ccs==null) return map;
// loop over compiled classes
Iterator it = ccs.iterator();
while (it.hasNext()) {
CompiledClass cc = (CompiledClass)it.next();
// map next
map.put(cc.getName(), cc);
// map it's inner classes
if (cc.inners!=null) map(map, cc.inners.values());
// map it's dependencies
if (cc.dependencies!=null) map(map, cc.dependencies.values());
}
// done
return map;
}
/**
* A classloader for the compiled class
*/
private class CL extends ClassLoader {
/** instantiated classes */
private Map classInstances = new HashMap();
/**
* Constructor
*/
private CL(ClassLoader parent) {
super(parent);
}
/**
* @see java.lang.ClassLoader#findClass(java.lang.String)
*/
protected Class findClass(String key) throws ClassNotFoundException {
// loaded it already?
Class result = (Class)classInstances.get(key);
if (result!=null) return result;
// maybe it's an inner type?
if (inners!=null) {
CompiledClass cc = (CompiledClass)inners.get(key);
// .. have to load with this classloader
if (cc!=null) return loadClass(cc);
}
// maybe it's a type we're depending on?
if (dependencies!=null) {
CompiledClass cc = (CompiledClass)dependencies.get(key);
// .. have to use it's classloader
if (cc!=null) return cc.getType();
}
// delegate
return super.findClass(key);
}
/**
* Loads the class from the contained byte-data
* @return the Class
*/
private Class loadClass() throws ClassFormatError {
return loadClass(CompiledClass.this);
}
/**
* Loads the class from given compiled class
*/
private Class loadClass(CompiledClass cc) throws ClassFormatError {
// Construct class
Class result = defineClass(null,cc.bytecode,0,cc.bytecode.length);
// Resolve it
try {
resolveClass(result);
} catch (IncompatibleClassChangeError err) {
// can't happen
}
// remember
classInstances.put(cc.name, result);
// Done
return result;
}
} //CL
} //CompiledClass
|