All Downloads are FREE. Search and download functionalities are using the official Maven repository.

serp.bytecode.Project Maven / Gradle / Ivy

There is a newer version: 4.0.0
Show newest version
package serp.bytecode;

import java.io.*;
import java.util.*;

import serp.bytecode.lowlevel.*;
import serp.bytecode.visitor.*;
import serp.util.*;

/**
 * The Project represents a working set of classes. It caches parsed
 * bytecode and is responsible for bytecode class creation. Currently
 * changes made in one class are not reflected in other
 * classes, though this will be an option in the future.
 *
 * 

Bytecode that has been parsed is held in a cache so that retrieving * a class with the same name multiple times always returns the same * {@link BCClass} instance.

* *

A future goal is to eventually have facilities for traversing jars * or directory structures to find classes that meet a given criteria (such * as implementing a given interface, etc) and to perform operations on entire * projects, similar to aspect-oriented programming.

* * @author Abe White */ public class Project implements VisitAcceptor { private final String _name; private final HashMap _cache = new HashMap(); private final NameCache _names = new NameCache(); /** * Default constructor. */ public Project() { this(null); } /** * Construct a named project. */ public Project(String name) { _name = name; } /** * Return the project name, or null if unset. */ public String getName() { return _name; } /** * Return the name cache, which includes utilities for converting names * from internal to external form and vice versa. */ public NameCache getNameCache() { return _names; } /** * Load a class with the given name. * * @see #loadClass(String,ClassLoader) */ public BCClass loadClass(String name) { return loadClass(name, null); } /** * Load the bytecode for the class with the given name. * If a {@link BCClass} with the given name already exists in this project, * it will be returned. Otherwise, a new {@link BCClass} will be created * with the given name and returned. If the name represents an existing * type, the returned instance will contain the parsed bytecode for * that type. If the name is of a primitive or array type, the returned * instance will act accordingly. * * @param name the name of the class, including package * @param loader the class loader to use to search for an existing * class with the given name; if null defaults to the * context loader of the current thread * @throws RuntimeException on parse error */ public BCClass loadClass(String name, ClassLoader loader) { // convert to proper Class.forName() form name = _names.getExternalForm(name, false); BCClass cached = checkCache(name); if (cached != null) return cached; // check for existing type if (loader == null) loader = Thread.currentThread().getContextClassLoader(); try { return loadClass(Strings.toClass(name, loader)); } catch (Exception e) { } String componentName = _names.getComponentName(name); BCClass ret = new BCClass(this); if (componentName != null) ret.setState(new ArrayState(name, componentName)); else { ret.setState(new ObjectState(_names)); ret.setName(name); ret.setSuperclass(Object.class); } cache(name, ret); return ret; } /** * Load the bytecode for the given class. * If a {@link BCClass} with the name of the given class already exists in * this project, it will be returned. Otherwise, the bytecode of the given * class will be parsed and returned as a new {@link BCClass}. If the * given class is an array or primitive type, the returned instance will * act accordingly. * * @param type the class to parse * @throws RuntimeException on parse error */ public BCClass loadClass(Class type) { BCClass cached = checkCache(type.getName()); if (cached != null) return cached; BCClass ret = new BCClass(this); if (type.isPrimitive()) ret.setState(new PrimitiveState(type, _names)); else if (type.isArray()) ret.setState(new ArrayState(type.getName(), _names.getExternalForm (type.getComponentType().getName(), false))); else { ret.setState(new ObjectState(_names)); try { ret.read(type); } catch (IOException ioe) { throw new RuntimeException(ioe.toString()); } } cache(type.getName(), ret); return ret; } /** * Load the bytecode from the given class file. * If this project already contains the class in the given file, it will * be returned. Otherwise a new {@link BCClass} will be created from the * given bytecode. * * @throws RuntimeException on parse error */ public BCClass loadClass(File classFile) { return loadClass(classFile, null); } /** * Load the bytecode from the given class file. * If this project already contains the class in the given file, it will * be returned. Otherwise a new {@link BCClass} will be created from the * given bytecode. * * @throws RuntimeException on parse error */ public BCClass loadClass(File classFile, ClassLoader loader) { // parse the bytecode from the file BCClass ret = new BCClass(this); ret.setState(new ObjectState(_names)); try { ret.read(classFile, loader); } catch (IOException ioe) { throw new RuntimeException(ioe.toString()); } String name = ret.getName(); BCClass cached = checkCache(name); if (cached != null) return cached; cache(name, ret); return ret; } /** * Load the bytecode from the given stream. * If this project already contains the class in the given stream, * it will be returned. Otherwise a new {@link BCClass} will be created * from the given bytecode. * * @throws RuntimeException on parse error */ public BCClass loadClass(InputStream in) { return loadClass(in, null); } /** * Load the bytecode from the given stream. * If this project already contains the class in the given stream, * it will be returned. Otherwise a new {@link BCClass} will be created * from the given bytecode. * * @throws RuntimeException on parse error */ public BCClass loadClass(InputStream in, ClassLoader loader) { BCClass ret = new BCClass(this); ret.setState(new ObjectState(_names)); try { ret.read(in, loader); } catch (IOException ioe) { throw new RuntimeException(ioe.toString()); } String name = ret.getName(); BCClass cached = checkCache(name); if (cached != null) return cached; cache(name, ret); return ret; } /** * Import the given bytecode from another project. If a {@link BCClass} * with the same name already exists in this project, it will be returned. * Otherwise, a new {@link BCClass} will be created from the * information in the given class. */ public BCClass loadClass(BCClass bc) { String name = bc.getName(); BCClass cached = checkCache(name); if (cached != null) return cached; BCClass ret = new BCClass(this); if (bc.isPrimitive()) ret.setState(new PrimitiveState(bc.getType(), _names)); else if (bc.isArray()) ret.setState(new ArrayState(bc.getName(), bc.getComponentName())); else { ret.setState(new ObjectState(_names)); ret.read(bc); } cache(name, ret); return ret; } /** * Clears all classes from this project. */ public void clear() { Collection values = _cache.values(); BCClass bc; for (Iterator itr = values.iterator(); itr.hasNext();) { bc = (BCClass) itr.next(); itr.remove(); bc.invalidate(); } _names.clear(); } /** * Remove a class from this project. After removal, the result of any * further operations on the class is undefined. * * @return true if the class belonged to this project, false otherwise */ public boolean removeClass(String type) { return removeClass(checkCache(type)); } /** * Remove a class from this project. After removal, the result of any * further operations on the class is undefined. * * @return true if the class belonged to this project, false otherwise */ public boolean removeClass(Class type) { if (type == null) return false; return removeClass(checkCache(type.getName())); } /** * Remove a class from this project. After removal, the result of any * further operations on the class is undefined. * * @return true if the class belonged to this project, false otherwise */ public boolean removeClass(BCClass type) { if (type == null) return false; if (!removeFromCache(type.getName(), type)) return false; type.invalidate(); return true; } /** * Return all loaded classes in the project. */ public BCClass[] getClasses() { Collection values = _cache.values(); return (BCClass[]) values.toArray(new BCClass[values.size()]); } /** * Return true if the project already contains the given class. */ public boolean containsClass(String type) { return _cache.containsKey(type); } /** * Return true if the project already contains the given class. */ public boolean containsClass(Class type) { return (type == null) ? false : containsClass(type.getName()); } /** * Return true if the project already contains the given class. */ public boolean containsClass(BCClass type) { return (type == null) ? false : containsClass(type.getName()); } public void acceptVisit(BCVisitor visit) { visit.enterProject(this); BCClass[] classes = getClasses(); for (int i = 0; i < classes.length; i++) classes[i].acceptVisit(visit); visit.exitProject(this); } /** * Renames the given class within this project. Used internally by * {@link BCClass} instances when their name is modified. * * @throws IllegalStateException if a class with the new name already exists */ void renameClass(String oldName, String newName, BCClass bc) { if (oldName.equals(newName)) return; BCClass cached = (BCClass) checkCache(newName); if (cached != null) throw new IllegalStateException("A class with name " + newName + " already exists in this project"); removeFromCache(oldName, bc); cache(newName, bc); } /** * Check the cache for a loaded type. */ private BCClass checkCache(String name) { return (BCClass) _cache.get(name); } /** * Cache a class. */ private void cache(String name, BCClass bc) { _cache.put(name, bc); } /** * Remove a cached class. */ private boolean removeFromCache(String name, BCClass bc) { BCClass rem = (BCClass) checkCache(name); if (rem != bc) return false; _cache.remove(name); return true; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy