package net.sourceforge.javautil.bytecode.api.type;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.sourceforge.javautil.bytecode.BytecodeCompiler;
import net.sourceforge.javautil.bytecode.BytecodeException;
import net.sourceforge.javautil.bytecode.BytecodeCompiler.Version;
import net.sourceforge.javautil.bytecode.api.IBytecodeAnnotated;
import net.sourceforge.javautil.bytecode.api.IBytecodeAnnotation;
import net.sourceforge.javautil.bytecode.api.IBytecodeField;
import net.sourceforge.javautil.bytecode.api.BytecodeFieldDeclared;
import net.sourceforge.javautil.bytecode.api.IBytecodeReferenceable;
import net.sourceforge.javautil.bytecode.api.BytecodeResolutionPool;
import net.sourceforge.javautil.bytecode.api.IBytecodeResolvable;
import net.sourceforge.javautil.bytecode.api.BytecodeResolvableAbstract;
import net.sourceforge.javautil.bytecode.api.IBytecodeSource;
import net.sourceforge.javautil.bytecode.api.IBytecodeWriter;
import net.sourceforge.javautil.bytecode.api.MethodDescriptor;
import net.sourceforge.javautil.bytecode.api.TypeDescriptor;
import net.sourceforge.javautil.bytecode.api.TypeMemberAccess;
import net.sourceforge.javautil.bytecode.api.IBytecodeAnnotation.AnnotationValue;
import net.sourceforge.javautil.bytecode.api.IBytecodeResolvable.ClassType;
import net.sourceforge.javautil.bytecode.api.TypeMemberAccess.Scope;
import net.sourceforge.javautil.bytecode.api.type.method.BytecodeBlock;
import net.sourceforge.javautil.bytecode.api.type.method.BytecodeEmbedded;
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.IBytecodeInstruction;
import net.sourceforge.javautil.bytecode.api.type.method.IBytecodeMethod;
import net.sourceforge.javautil.bytecode.api.type.method.BytecodeMethodConcrete;
import net.sourceforge.javautil.bytecode.api.type.method.IBytecodeMethod.ArgumentMatch;
/**
* The base interface for all Java types defined in this framework.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public abstract class AbstractType<C extends BytecodeContextType> extends BytecodeResolvableAbstract implements IBytecodeSource<C>, IBytecodeResolvable {
protected final TypeMemberAccess access;
protected final BytecodeCompiler compiler;
protected IBytecodeResolvable superType;
protected Set<IBytecodeResolvable> interfaces = new LinkedHashSet<IBytecodeResolvable>();
protected Map<String, BytecodeFieldDeclaration> fields = new LinkedHashMap<String, BytecodeFieldDeclaration>();
protected Map<String, IBytecodeReferenceable> defaultStaticValues = new HashMap<String, IBytecodeReferenceable>();
protected List<IBytecodeMethod> methods = new ArrayList<IBytecodeMethod>();
protected Set<BytecodeAnnotationDeclaration> annotations = new LinkedHashSet<BytecodeAnnotationDeclaration>();
protected AbstractType(BytecodeCompiler compiler, String name, TypeMemberAccess access, ClassType type) {
super(type, TypeDescriptor.getFor(name));
this.access = access;
this.compiler = compiler;
}
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() { return Collections.EMPTY_SET; }
public Set<IBytecodeField> getDeclaredFields() {
return new LinkedHashSet<IBytecodeField>( fields.values() );
}
public Set<IBytecodeMethod> getDeclaredMethods() {
return new LinkedHashSet<IBytecodeMethod>(methods);
}
public Set<BytecodeAnnotationDeclaration> getDeclaredAnnotations() {
return Collections.unmodifiableSet( this.annotations );
}
/**
* Facility for linkable annotation declarations.
*
* @see #annotate(IBytecodeResolvable)
*/
public BytecodeAnnotationDeclaration annotate (Class<? extends Annotation> annotationType) {
return this.annotate(this.compiler.getPool().resolve(annotationType.getName()));
}
/**
* @param annotationType The type of the annotation
* @return The newly created annotation declaration
*/
public BytecodeAnnotationDeclaration annotate (IBytecodeResolvable annotationType) {
BytecodeAnnotationDeclaration bad = new BytecodeAnnotationDeclaration(annotationType);
this.annotations.add(bad);
return bad;
}
/**
* Copy all of the annotations found on the annotated member.
*
* @param annotated The annotated from which to copy the annotations
*/
public void copyAnnotations (IBytecodeAnnotated annotated) {
for (IBytecodeAnnotation annotation : annotated.getDeclaredAnnotations()) {
BytecodeAnnotationDeclaration bad = this.annotate(annotation.getType());
Map<String, AnnotationValue> values = annotation.getValues();
for (String name : values.keySet()) {
bad.setValue(name, values.get(name).getValue());
}
}
}
/**
* @param name The name of the static field
* @param type The type of the field
* @param initialValue The initial value
* @return The delcaration for the field
*/
public IBytecodeField addStaticField (String name, Class type, Scope scope, boolean isFinal, IBytecodeReferenceable defaultValue) {
this.fields.put(name, new BytecodeFieldDeclaration(name, new TypeMemberAccess(scope, false, true, isFinal), this, TypeDescriptor.getFor(type)));
if (defaultValue != null)
this.defaultStaticValues.put(name, defaultValue);
return this.fields.get(name);
}
/**
* @return The names of the fields for this type
*/
public Set<String> getFieldNames () { return fields.keySet(); }
public IBytecodeField getField (BytecodeResolutionPool pool, String name) {
IBytecodeField thisField = this.fields.get(name);
if (thisField == null && superType != null) {
thisField = this.superType.getField(pool, name);
}
return thisField;
}
/**
* @return The amount of methods declared in this type
*/
public int getMethodCount () {
return this.methods.size();
}
/**
* @param idx The index of the method
* @return The corresponding method
*/
public IBytecodeMethod getMethod (int idx) {
return this.methods.get(idx);
}
public void write(C context) {
context.getWriter().begin(context);
for (String field : this.fields.keySet()) {
context.getWriter().declareField(context, field, this.fields.get(field));
}
final BytecodeEmbedded cinit = this.getClassInitializer();
if (cinit != null || this.defaultStaticValues.size() > 0) {
BytecodeMethodConcrete clinit = new BytecodeMethodConcrete(this, "<clinit>", new TypeMemberAccess(Scope.Private, false, true, true),
new MethodDescriptor(false, void.class, new Class[0], new Class[0]));
clinit.setMethodBody(new BytecodeBlock() {
@Override protected void writeInstructions(BytecodeContextMethod context) {
for (String name : defaultStaticValues.keySet()) {
context.set(context.getStaticField(name), defaultStaticValues.get(name));
}
if (cinit != null) cinit.write(context);
}
});
context.getWriter().writeMethod(context, clinit);
}
this.writeInternal(context);
context.getWriter().end(context);
}
/**
* @return The interfaces implemented/extended for this type
*/
public IBytecodeResolvable[] getInterfaces() { return interfaces.toArray(new IBytecodeResolvable[interfaces.size()]); }
/**
* @param context The context in which to write the type body
*/
protected void writeInternal (C context) {
for (IBytecodeMethod method : this.methods) {
context.getWriter().writeMethod(context, method);
}
}
public IBytecodeResolvable getSuperType() { return superType; }
/**
* @return The java class name for this type
*/
public String getName() { return descriptor.getClassName(); }
/**
* @return The generated class
*/
public Class compile () { return compiler.compile(this); }
/**
* @param version The version to compile to
* @return The generated class
*/
public Class compile (Version version) { return compiler.compile(this, version); }
/**
* @param name The name of the new type
* @param isStatic True if the cloned type should be static
* @param isFinal True if the cloned type should be final
* @return A type with the same fields, interfaces, super type, methods and constructors
*/
public abstract AbstractType cloneAs (String name, boolean isStatic, boolean isFinal);
/**
* @param target The target type to which to copy internal class information
*/
protected void internalClone (AbstractType target) {
target.fields.putAll(this.fields);
target.interfaces.addAll(this.interfaces);
target.methods.addAll(this.methods);
target.superType = this.superType;
target.defaultStaticValues.putAll(this.defaultStaticValues);
}
/**
* @return The access rules for this type
*/
public TypeMemberAccess getAccess() {
return access;
}
public boolean isInstanceof(BytecodeResolutionPool pool, IBytecodeResolvable resolvable) {
if (this.superType != null) {
boolean isst = this.superType.isInstanceof(pool, resolvable);
if (isst) return true;
}
for (IBytecodeResolvable iface : this.interfaces) {
if (iface.isInstanceof(pool, resolvable)) return true;
}
if (resolvable.getType().getName().equals(Object.class.getName())) return true;
return false;
}
public IBytecodeConstructor findConstructor(BytecodeResolutionPool pool, TypeDescriptor... parameters) {
throw new BytecodeException("Cannot instantiate this type: " + getName());
}
public IBytecodeMethod findMethod(BytecodeResolutionPool pool, String name, TypeDescriptor... parameters) {
IBytecodeMethod bm = null;
for (IBytecodeMethod method : this.methods) {
if (!method.getName().equals(name)) continue;
ArgumentMatch am = method.compareArguments(pool, parameters);
if (am == ArgumentMatch.FUNCTIONAL && bm == null) bm = method;
else if (am == ArgumentMatch.EXACT) { bm = method; break; }
}
if (bm == null) {
IBytecodeResolvable st = this.superType;
if (st == null) st = pool.resolve(Object.class.getName());
return st.findMethod(pool, name, parameters);
}
return bm;
}
/**
* This will be called when the static initializer is being written and after
* any static field values have been assigned default values if any.
*
* @return Embedded source code instructions to be written in the static class initializer method, or null if none to write
*/
protected BytecodeEmbedded getClassInitializer () { return null; };
}