More support for method's source display. Not yet working.
The behaviour can (whether to show full class source or just method's source)
be now controlled by JavaMethod class>>showFullSource: More work has to be done to fully support this.
package stx.libjava.tools.environment;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
public class Environment implements INameEnvironment {
protected static Environment SHARED;
protected ClassLoader classloader;
public TypeRegistry types;
public static synchronized Environment shared() {
if (SHARED == null) {
SHARED = new Environment();
};
return SHARED;
}
public Environment() {
this(new TypeRegistry(), ClassLoader.getSystemClassLoader() );
}
public Environment(TypeRegistry types) {
this(types, ClassLoader.getSystemClassLoader());
}
public Environment(ClassLoader cl) {
this(new TypeRegistry(), cl);
}
public Environment(TypeRegistry types, ClassLoader cl) {
this.types = types;
this.classloader = cl;
}
public ClassLoader getClassLoader() {
return classloader;
}
/**
* Find a type with the given compound name.
* Answer the binary form of the type if it is known to be consistent.
* Otherwise, answer the compilation unit which defines the type
* or null if the type does not exist.
* Types in the default package are specified as {{typeName}}.
*
* It is unknown whether the package containing the type actually exists.
*
* NOTE: This method can be used to find a member type using its
* internal name A$B, but the source file for A is answered if the binary
* file is inconsistent.
*/
public NameEnvironmentAnswer findType(char[][] compoundTypeName) {
StringBuilder sb = new StringBuilder();
if (compoundTypeName.length > 1) {
for (int i = 0; i < compoundTypeName.length - 1; i++) {
sb.append(compoundTypeName[i]);
sb.append('.');
}
}
sb.append(compoundTypeName[compoundTypeName.length-1]);
return findType(sb.toString());
}
/**
* Returns a IBinaryType for class with given name or null
* if there's no such class.
* @param name
* @return binary type or null (if no type is found)
*/
protected IBinaryType findTypeForClassNamed(String name) {
IBinaryType type;
type = types.get(name);
if (type != null) return type;
try {
Class<?> c = Class.forName(name, false, classloader);
InputStream cfs = findClassFileForClass(c);
if (cfs == null) {
return null;
} else {
types.put(name, cfs);
return types.get(name);
}
} catch (ClassNotFoundException cnfe) {
return null;
} catch (RuntimeException e) {
e.printStackTrace();
return null;
}
}
protected NameEnvironmentAnswer findType(String name) {
IBinaryType type = findTypeForClassNamed(name);
if (type != null) {
return new NameEnvironmentAnswer(type, null);
} else {
return null;
}
}
/**
* Find a type named <typeName> in the package <packageName>.
* Answer the binary form of the type if it is known to be consistent.
* Otherwise, answer the compilation unit which defines the type
* or null if the type does not exist.
* The default package is indicated by char[0][].
*
* It is known that the package containing the type exists.
*
* NOTE: This method can be used to find a member type using its
* internal name A$B, but the source file for A is answered if the binary
* file is inconsistent.
*/
public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < packageName.length; i++) {
sb.append(packageName[i]);
sb.append('.');
}
sb.append(typeName);
return findType(sb.toString());
}
/**
* Answer whether packageName is the name of a known subpackage inside
* the package parentPackageName. A top level package is found relative to null.
* The default package is always assumed to exist.
*
* For example:
* isPackage({{java}, {awt}}, {event});
* isPackage(null, {java});
*/
public boolean isPackage(char[][] parentPackageName, char[] packageName) {
return Character.isLowerCase(packageName[0]);
}
/**
* This method cleans the environment uo. It is responsible for releasing the memory
* and freeing resources. Passed that point, the name environment is no longer usable.
*
* A name environment can have a long life cycle, therefore it is the responsibility of
* the code which created it to decide when it is a good time to clean it up.
*/
public void cleanup() {
}
/**
* Given a class, return an InputStream on corresponding .class file.
* @return InputStream or null
*/
public InputStream findClassFileForClass(Class<?> c) {
/* STX:LIBJAVA specific: ask the class for its classfile bytes... */
if (System.getProperty("java.vm.name").equals("Smalltalk/X")) {
byte[] bytes = findClassBytesForClass0(c);
if (bytes != null) {
return new ByteArrayInputStream(bytes);
}
}
ClassLoader cl = c.getClassLoader();
if (cl == null) {
cl = ClassLoader.getSystemClassLoader();
}
String cfilename = (new String(c.getName())).replace('.', '/') + ".class";
return cl.getResourceAsStream(cfilename);
}
public native byte[] findClassBytesForClass0(Class<?> c);
/**
* A registry for binary types. Eventually will do some caching in the future...
*
* @author Jan Vrany
*
*/
public static class TypeRegistry {
protected Map<String, IBinaryType> typeMap = new HashMap<String, IBinaryType>();
public IBinaryType get(String name) {
return typeMap.get(name);
}
public void put(String name, IBinaryType type) {
typeMap.put(name,type);
}
public void put(String name, InputStream classfile) {
try {
put(name, ClassFileReader.read(classfile, (new String(name).replace('.', '/') + ".class")));
} catch (ClassFormatException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void put(String name, byte[] classfile) {
put(name, new ByteArrayInputStream(classfile));
}
}
}