package net.sourceforge.javautil.bytecode.api;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import bsh.Modifiers;
import net.sourceforge.javautil.bytecode.BytecodeException;
import net.sourceforge.javautil.bytecode.api.IBytecodeResolvable.ClassType;
import net.sourceforge.javautil.bytecode.api.TypeMemberAccess.Scope;
import net.sourceforge.javautil.bytecode.api.type.AbstractType;
import net.sourceforge.javautil.bytecode.api.type.method.IBytecodeConstructor;
import net.sourceforge.javautil.bytecode.api.type.method.BytecodeContextMethod;
import net.sourceforge.javautil.bytecode.api.type.method.IBytecodeMethod;
import net.sourceforge.javautil.bytecode.api.type.method.IBytecodeMethod.ArgumentMatch;
import net.sourceforge.javautil.bytecode.api.type.method.invocation.MethodInvocation;
/**
* A pool in which {@link IBytecodeResolvable}'s are grouped for resolution.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class BytecodeResolutionPool {
/**
* @param clazz The clazz
* @return The related enum class type
*/
public static ClassType getClassTypeFor (Class<?> clazz) {
if (clazz.isInterface()) return ClassType.Interface;
if (clazz.isEnum()) return ClassType.Enum;
if (clazz.isAnnotation()) return ClassType.Annotation;
return ClassType.Class;
}
protected final ClassLoader parentLoader;
protected Map<String, IBytecodeResolvable> resolved = new HashMap<String, IBytecodeResolvable>();
public BytecodeResolutionPool(ClassLoader parentLoader) {
this.parentLoader = parentLoader;
}
/**
* @param className The className association to clear
*/
public void clear (String className) {
this.resolved.remove(className);
}
/**
* @param className The class name to check for
* @return True if the class by this name has already been resolved
*/
public boolean isResolved (String className) {
return resolved.containsKey(className);
}
/**
* @param type The type to add to this pool
*/
public void add (AbstractType type) {
if (resolved.containsKey(type.getName())) {
throw new BytecodeException("This pool already has a class by the name of: " + type.getName());
}
this.resolved.put(type.getName(), type);
}
/**
* @param types The types to resolve
* @return The array of resolved types
*/
public IBytecodeResolvable[] resolve (TypeDescriptor... types) {
IBytecodeResolvable[] resolved = new IBytecodeResolvable[types.length];
for (int r=0; r<resolved.length; r++) {
resolved[r] = resolve(types[r].getClassName());
}
return resolved;
}
/**
* @param classNames The name of the classes to resolve
* @return The resolved array of classes
*/
public IBytecodeResolvable[] resolve (String... classNames) {
IBytecodeResolvable[] resolved = new IBytecodeResolvable[classNames.length];
for (int r=0; r<resolved.length; r++) {
resolved[r] = resolve(classNames[r]);
}
return resolved;
}
/**
* @param clazz The class for which to get a linkable wrapper
* @return The linkable wrapper
*/
public IBytecodeResolvable resolve (String className) {
IBytecodeResolvable resolved = this.resolved.get(className);
if (resolved == null) {
try {
Class clazz = null;
if ("boolean".equals(className)) clazz = boolean.class;
else if ("int".equals(className)) clazz = int.class;
else if ("byte".equals(className)) clazz = byte.class;
else if ("short".equals(className)) clazz = short.class;
else if ("long".equals(className)) clazz = long.class;
else if ("float".equals(className)) clazz = float.class;
else if ("double".equals(className)) clazz = double.class;
else if ("char".equals(className)) clazz = char.class;
else clazz = Class.forName(className, false, parentLoader);
this.resolved.put(className, resolved = new Linkable(clazz));
} catch (ClassNotFoundException e) {
throw new BytecodeException("Could not resolve class: " + className, e);
}
}
return resolved;
}
/**
* The wrapper for {@link Class}'s to be dealt with as {@link IBytecodeResolvable}'s.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class Linkable extends BytecodeResolvableAbstract {
protected final Class<?> clazz;
protected final IBytecodeResolvable superType;
protected final TypeMemberAccess access;
protected final Map<String, MethodWrapper> wrappers = new HashMap<String, MethodWrapper>();
public Linkable(Class clazz) {
super(getClassTypeFor(clazz), TypeDescriptor.getFor(clazz));
this.clazz = clazz;
this.access = TypeMemberAccess.getFor(clazz.getModifiers());
this.superType = clazz.getSuperclass() == null ? null : resolve(clazz.getSuperclass().getName());
}
/**
* @return The clazz wrapped by this resolvable impl
*/
public Class<?> getWrapped() { return clazz; }
public Set<BytecodeAnnotationDeclared> getDeclaredAnnotations() {
return BytecodeAnnotationDeclared.getAnnotations(BytecodeResolutionPool.this, clazz);
}
public Scope getAccessScope() { return access.getScope(); }
public boolean isFinal() { return access.isFinal(); }
public boolean isStatic() { return access.isStatic(); }
public boolean isAbstract() { return access.isAbstract(); }
public Set<IBytecodeConstructor> getDeclaredConstructors() {
Set<IBytecodeConstructor> constructors = new LinkedHashSet<IBytecodeConstructor>();
for (Constructor constructor : clazz.getDeclaredConstructors()) {
constructors.add(new ConstructorWrapper(constructor));
}
return constructors;
}
public Set<IBytecodeField> getDeclaredFields() {
Set<IBytecodeField> fields = new LinkedHashSet<IBytecodeField>();
for (Field field : clazz.getDeclaredFields()) {
fields.add(new BytecodeFieldDeclared(BytecodeResolutionPool.this, this, field));
}
return fields;
}
public Set<IBytecodeMethod> getDeclaredMethods() {
Set<IBytecodeMethod> methods = new LinkedHashSet<IBytecodeMethod>();
for (Method method : clazz.getDeclaredMethods()) {
methods.add(new MethodWrapper(method));
}
return methods;
}
public IBytecodeField getField(BytecodeResolutionPool pool, String name) {
try {
Field field = clazz.getDeclaredField(name);
return new BytecodeFieldDeclared(pool, this, field);
} catch (SecurityException e) {
throw new BytecodeException("Could not access field: " + name, e);
} catch (NoSuchFieldException e) {
if (this.superType != null) {
return this.getSuperType().getField(pool, name);
} else {
throw new RuntimeException(e);
}
}
}
public IBytecodeConstructor findConstructor(BytecodeResolutionPool pool, TypeDescriptor... types) {
ConstructorWrapper cw = null;
for (Constructor constructor : clazz.getConstructors()) {
ConstructorWrapper wrapper = new ConstructorWrapper(constructor);
ArgumentMatch am = wrapper.compareArguments(pool, types);
if (am == ArgumentMatch.FUNCTIONAL && cw == null) cw = wrapper;
else if (am == ArgumentMatch.EXACT) { cw = wrapper; break; }
}
return cw;
}
public IBytecodeMethod findMethod(BytecodeResolutionPool pool, String name, TypeDescriptor... types) {
MethodWrapper mw = null;
try {
Class[] parameterTypes = new Class[types.length];
for (int i=0; i<types.length; i++) {
parameterTypes[i] = Class.forName(types[i].getClassName());
}
return new MethodWrapper( clazz.getMethod(name, parameterTypes) );
}
catch (ClassNotFoundException e) {}
catch (NoSuchMethodException e) {}
Class sc = clazz;
ArgumentMatch am = null;
while (sc != null) {
Method[] methods = sc.isInterface() ? sc.getMethods() : sc.getDeclaredMethods();
for (Method method : methods) {
if (!method.getName().equals(name)) continue;
MethodWrapper wrapper = new MethodWrapper(method);
am = wrapper.compareArguments(pool, types);
if (am == ArgumentMatch.FUNCTIONAL && mw == null) mw = wrapper;
else if (am == ArgumentMatch.EXACT) {
mw = wrapper;
if (!method.isBridge()) break;
}
}
if (am == ArgumentMatch.EXACT) break;
sc = sc.getSuperclass();
}
return mw;
}
public boolean isInstanceof(BytecodeResolutionPool pool, IBytecodeResolvable resolvable) {
return resolvable instanceof Linkable ? ((Linkable)resolvable).clazz.isAssignableFrom(clazz) : false;
}
public IBytecodeResolvable getSuperType() {
return superType;
}
/**
* This will wrap a constructor for integration with {@link IBytecodeConstructor} API.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class ConstructorWrapper extends BytecodeMemberDeclared implements IBytecodeConstructor {
protected final Constructor constructor;
protected final MethodDescriptor descriptor;
protected final TypeMemberAccess access;
public ConstructorWrapper(Constructor constructor) {
super(BytecodeResolutionPool.this, constructor);
this.constructor = constructor;
this.descriptor = new MethodDescriptor(constructor.isVarArgs(), TypeDescriptor.VOID, TypeDescriptor.getFor(constructor.getExceptionTypes()), TypeDescriptor.getFor(constructor.getParameterTypes()));
this.access = TypeMemberAccess.getFor(constructor.getModifiers());
}
public boolean isVarArgs() { return constructor.isVarArgs(); }
public IBytecodeResolvable getDeclaringType() { return Linkable.this; }
public MethodDescriptor getDescriptor() { return this.descriptor; }
public String getName() { return "<init>"; }
public TypeMemberAccess getAccess() { return this.access; }
public ArgumentMatch compareArguments (BytecodeResolutionPool pool, TypeDescriptor... types) {
return Util.compareArguments(pool, this, types);
}
}
/**
* A wrapper for a class method.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class MethodWrapper extends BytecodeMemberDeclared implements IBytecodeMethod {
protected final Method method;
protected final MethodDescriptor descriptor;
protected final TypeMemberAccess access;
public MethodWrapper(Method method) {
super(BytecodeResolutionPool.this, method);
this.method = method;
this.descriptor = new MethodDescriptor(method);
this.access = TypeMemberAccess.getFor(method.getModifiers());
}
public boolean isVarArgs() { return descriptor.isVarArgs(); }
public IBytecodeResolvable getDeclaringType() { return Linkable.this; }
public MethodDescriptor getDescriptor() { return this.descriptor; }
public String getName() { return method.getName(); }
public TypeMemberAccess getAccess() { return this.access; }
public ArgumentMatch compareArguments (BytecodeResolutionPool pool, TypeDescriptor... types) {
return Util.compareArguments(pool, this, types);
}
}
}
}