tools/java/src/stx/libjava/tools/environment/Environment.java
author Jan Vrany <jan.vrany@fit.cvut.cz>
Sat, 14 Sep 2013 15:48:03 +0100
branchdevelopment
changeset 2729 ac412f6ea6d4
parent 2728 658220e93dc9
child 2732 7d1a1fb5b01a
permissions -rw-r--r--
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));
	    }
	}

}