package net.sourceforge.retroweaver.runtime.java.lang.reflect;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import net.sourceforge.retroweaver.runtime.java.lang.TypeNotPresentException;
import net.sourceforge.retroweaver.runtime.java.lang.annotation.AIB;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.signature.SignatureReader;
import org.objectweb.asm.signature.SignatureVisitor;

public class ReflectionDescriptor implements ClassVisitor {

	private static final boolean DEBUG = false;

	private static final Map<Class, ReflectionDescriptor> descriptors = new HashMap<Class, ReflectionDescriptor>();

	public static ReflectionDescriptor getReflectionDescriptor(Class class_) {
		synchronized (descriptors) {
			ReflectionDescriptor d = descriptors.get(class_);
			if (d == null) {
				d = new ReflectionDescriptor(class_);
				descriptors.put(class_, d);
				if (DEBUG) d.debugMessage("Adding descriptor");
			}
			return d;			
		}
	}

	private final Class class_;

	private ReflectionDescriptor(Class class_) {
		this.class_ = class_;
		String name = class_.getName();

		String resource = "/" + name.replace('.', '/') + ".class";
if (DEBUG) System.out.println("Reading class file: " + resource);
		InputStream classStream = class_.getResourceAsStream(resource);
		parseStream(name, classStream);
	}

	protected ReflectionDescriptor(String name, InputStream classStream) {
		class_ = null;
		parseStream(name, classStream);
	}
	
	private void parseStream(String name, InputStream classStream) {
		try {
			ClassReader r = new ClassReader(classStream);
			r.accept(this, ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG
					+ ClassReader.SKIP_FRAMES);

		} catch (IOException e) {
			// Shouldn't generally happen
			throw new TypeNotPresentException(
					"[Retroweaver] Unable to read reflection data for: " + name, e);
		} finally {
			try {
				if (classStream != null) {
					classStream.close();
				}
			} catch (IOException e) { // NOPMD by xlv
			}
		}
	}

	private String internalName;

	private String enclosingClassName;

	private String enclosingMethodName;

	private String enclosingMethodDesc;

	private HashMap<String, Integer> fieldAccessTable = new HashMap<String, Integer>();

	private HashMap<String, Integer> methodAccessTable = new HashMap<String, Integer>();

	public String getEnclosingClassName() {
		return enclosingClassName;
	}

	public void debugMessage(String msg) {
		System.out.println(msg +
				"\n\tclass: " + class_.getName() +
				"\n\tenclosingClassName: " + enclosingClassName +
				"\n\tenclosingMethodName: " + enclosingMethodName + ' ' + enclosingMethodDesc);
	}

	public Class getEnclosingClass() {
		//debugMessage("getEnclosingClass");
		if (enclosingClassName == null) {
			return null;
		}

		try {
			//debugMessage("getEnclosingClass");
			String name = enclosingClassName.replace('/', '.');
			Class c = class_.getClassLoader().loadClass(name);
		
			return c;
		} catch (ClassNotFoundException e) {
			return null;
		}
	}

	public Method getEnclosingMethod() {
		//debugMessage("getEnclosingMethod");
		if (enclosingMethodName == null) {
			return null;
		}

		return getMethod(getEnclosingClass(), enclosingMethodName, enclosingMethodDesc);
	}

	public Constructor getEnclosingConstructor() {
		//debugMessage("getEnclosingMethod");
		if (enclosingMethodName == null) {
			return null;
		}

		return getConstructor(getEnclosingClass(), enclosingMethodDesc);
	}

	public boolean testFieldAccess(Field f, int mask) {
		String name = f.getName();
		return (fieldAccessTable.get(name).intValue() & mask) != 0;
	}

	public boolean testMethodAccess(Method m, int mask) {
		String name = m.getName();
		String desc = org.objectweb.asm.Type.getMethodDescriptor(m);
		return (methodAccessTable.get(name+desc).intValue() & mask) != 0;
	}

	public boolean testConstructorAccess(Constructor c, int mask) {
		String name = "<init>";
		String desc = org.objectweb.asm.Type.getConstructorDescriptor(c);
		return (methodAccessTable.get(name+desc).intValue() & mask) != 0;
	}

	private Method getMethod(Class class_, String name, String desc) {
		org.objectweb.asm.Type[] types = org.objectweb.asm.Type.getArgumentTypes(desc);

	outer_loop:
		for (Method m : class_.getDeclaredMethods()) {
			final org.objectweb.asm.Type[] methodTypes = org.objectweb.asm.Type.getArgumentTypes(m);
			if (!m.getName().equals(name)
					|| methodTypes.length != types.length) {
				continue;
			}
			for (int i = 0; i < types.length; ++i) {
				if (!types[i].equals(methodTypes[i])) {
					continue outer_loop;
				}
			}
			return m;
		}
		return null;
	}

	private Constructor getConstructor(Class class_, String desc) {
		org.objectweb.asm.Type[] types = org.objectweb.asm.Type.getArgumentTypes(desc);

	outer_loop:
		for (Constructor c : class_.getDeclaredConstructors()) {
			final Class[] constructorTypes = c.getParameterTypes();
			if (constructorTypes.length != types.length) {
				continue;
			}
			for (int i = 0; i < types.length; ++i) {
				if (!types[i].equals(org.objectweb.asm.Type.getType(constructorTypes[i]))) {
					continue outer_loop;
				}
			}
			return c;
		}
		return null;
	}

	private void parseSignature(String signature, boolean isInterface, String superName, String[] interfaces) {
		if (signature == null) {
			typeParameters = new TypeVariable[0];
			genericSuperclass = superName==null?null:new ClassTypeImpl(superName.replaceAll("/", "."));
			if (interfaces == null) {
				genericInterfaces = new Type[0];
			} else {
				genericInterfaces = new Type[interfaces.length];
				for(int i = 0; i < interfaces.length; i++) {
					genericInterfaces[i] = new ClassTypeImpl(interfaces[i].replaceAll("/", "."), true);
				}
			}
		} else {
			if (DEBUG) System.out.println("Parsing " + signature);
			SignatureReader r = new SignatureReader(signature);
			ClassTypeImpl currentType = new ClassTypeImpl(internalName.replaceAll("/", "."), isInterface);
			SigVisitor v = new SigVisitor(currentType, false);
			r.accept(v);
			v.endParsing();
			
			typeParameters = v.typeParameters;
			genericSuperclass = v.genericSuperclass;
			genericInterfaces = v.genericInterfaces;
		}
		if (isInterface) {
			genericSuperclass = null;
		}

	}

	private TypeVariable[] typeParameters;

	private Type genericSuperclass;

	private Type[] genericInterfaces;

	public TypeVariable[] getTypeParameters() throws GenericSignatureFormatError {
		return typeParameters;
	}

	public Type getGenericSuperclass() throws GenericSignatureFormatError, TypeNotPresentException, MalformedParameterizedTypeException {
		return genericSuperclass;
	}

	public Type[] getGenericInterfaces() throws GenericSignatureFormatError, TypeNotPresentException, MalformedParameterizedTypeException {
		return genericInterfaces;
	}

    /**
     * Visits the header of the class.
     * 
     * @param version the class version.
     * @param access the class's access flags (see {@link Opcodes}). This
     *        parameter also indicates if the class is deprecated.
     * @param name the internal name of the class (see
     *        {@link Type#getInternalName() getInternalName}).
     * @param signature the signature of this class. May be <tt>null</tt> if
     *        the class is not a generic one, and does not extend or implement
     *        generic classes or interfaces.
     * @param superName the internal of name of the super class (see
     *        {@link Type#getInternalName() getInternalName}). For interfaces,
     *        the super class is {@link Object}. May be <tt>null</tt>, but
     *        only for the {@link Object} class.
     * @param interfaces the internal names of the class's interfaces (see
     *        {@link Type#getInternalName() getInternalName}). May be
     *        <tt>null</tt>.
     */
    public void visit(
            int version,
            int access,
            String name,
            String signature,
            String superName,
            String[] interfaces) {
    	internalName = name;
    	boolean isInterface = ( access & Opcodes.ACC_INTERFACE ) == Opcodes.ACC_INTERFACE;
    	parseSignature(signature, isInterface, superName, interfaces);
    }

    public void visitSource(String source, String debug) {}

    /**
     * Visits the enclosing class of the class. This method must be called only
     * if the class has an enclosing class.
     * 
     * @param owner internal name of the enclosing class of the class.
     * @param name the name of the method that contains the class, or
     *        <tt>null</tt> if the class is not enclosed in a method of its
     *        enclosing class.
     * @param desc the descriptor of the method that contains the class, or
     *        <tt>null</tt> if the class is not enclosed in a method of its
     *        enclosing class.
     */
    public void visitOuterClass(String owner, String name, String desc) {
    	enclosingClassName = owner;
    	enclosingMethodName = name;
    	enclosingMethodDesc = desc;
    	//debugMessage("visitOuterClass");
    }

    /**
     * Visits information about an inner class. This inner class is not
     * necessarily a member of the class being visited.
     * 
     * @param name the internal name of an inner class (see
     *        {@link Type#getInternalName() getInternalName}).
     * @param outerName the internal name of the class to which the inner class
     *        belongs (see {@link Type#getInternalName() getInternalName}). May
     *        be <tt>null</tt>.
     * @param innerName the (simple) name of the inner class inside its
     *        enclosing class. May be <tt>null</tt> for anonymous inner
     *        classes.
     * @param access the access flags of the inner class as originally declared
     *        in the enclosing class.
     */
    public void visitInnerClass(
            String name,
            String outerName,
            String innerName,
            int access) {
    	if (name.equals(internalName)) {
    		if (outerName != null) {
    			enclosingClassName = outerName;
    		}
    	}
    	//debugMessage("visitInnerClass");
    }

    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
    	return AIB.EMPTY_VISITOR;
    }

    public void visitAttribute(Attribute attr) {}

    public FieldVisitor visitField(
            int access,
            String name,
            String desc,
            String signature,
            Object value) {
    	fieldAccessTable.put(name, access);
    	return null;
    }

    public MethodVisitor visitMethod(
            int access,
            String name,
            String desc,
            String signature,
            String[] exceptions) {
    	methodAccessTable.put(name+desc, access);
    	return null;
    }

    public void visitEnd() {}

    public static void main(String args[]) {
    	String signature = "<E:Ljava/lang/String;>Ljava/util/LinkedList<TE;>;Ljava/io/Serializable;Ljava/lang/Comparable<TE;>;";
    	String internalName = "blah.TopLevel";

    	System.out.println("Parsing " + signature);
		SignatureReader r = new SignatureReader(signature);
		ClassTypeImpl currentType = new ClassTypeImpl(internalName.replaceAll("/", "."), false);
		SigVisitor v = new SigVisitor(currentType, false);
		r.accept(v);
		v.endParsing();
		
		System.out.println(v.genericInterfaces.length);
		for(Type t: v.genericInterfaces) {
			System.out.println(t);
		}
    }

    private static class SigVisitor implements SignatureVisitor {
		protected TypeVariable[] typeParameters;
		protected Type genericSuperclass;
		protected Type[] genericInterfaces;
	
		private Stack<Type> stack = new Stack<Type>();

		private Set<TypeVariableImpl> typeVariables = new HashSet<TypeVariableImpl>();

		private LinkedList<TypeVariableImpl> formalTypeParameters = new LinkedList<TypeVariableImpl>();

		private StringBuffer declaration;

	    private boolean isInterface;

	    private ClassTypeImpl currentType;

	    private boolean seenFormalParameter;

	    private boolean seenInterfaceBound;

	    private boolean seenParameter;

	    private boolean seenInterface;

	    private StringBuffer returnType;

	    private StringBuffer exceptions;

	    /**
	     * Stack used to keep track of class types that have arguments. Each element
	     * of this stack is a boolean encoded in one bit. The top of the stack is
	     * the lowest order bit. Pushing false = *2, pushing true = *2+1, popping =
	     * /2.
	     */
	    private int argumentStack;

	    private Stack<Integer> argumentStackPosition = new Stack<Integer>();

	    /**
	     * Stack used to keep track of array class types. Each element of this stack
	     * is a boolean encoded in one bit. The top of the stack is the lowest order
	     * bit. Pushing false = *2, pushing true = *2+1, popping = /2.
	     */
	    private int arrayStack;

	    private String separator = "";

	    public SigVisitor(final ClassTypeImpl currentType, final boolean isInterface) {
	        this.isInterface = isInterface;
	        this.declaration = new StringBuffer();
	        this.currentType = currentType;
	    }

	    private SigVisitor(final StringBuffer buf) {
	        this.declaration = buf;
	    }

	    /**
	     * Visits a formal type parameter.
	     * 
	     * @param name the name of the formal parameter.
	     */
	    public void visitFormalTypeParameter(final String name) {
	    	if (seenFormalParameter) {
	    		endFormal();
	    	}
	    	TypeVariableImpl t = new TypeVariableImpl(name);
	    	typeVariables.add(t);
	    	formalTypeParameters.add(t);
	    	if (DEBUG) System.out.println("pushing visitFormalTypeParameter " + t);

	    	declaration.append(seenFormalParameter ? ", " : "<").append(name);
	        seenFormalParameter = true;
	        seenInterfaceBound = false;
	    }

	    /**
	     * Visits the class bound of the last visited formal type parameter.
	     * 
	     * @return a non null visitor to visit the signature of the class bound.
	     */
	    public SignatureVisitor visitClassBound() {
	        separator = " extends ";
	        startType();
	        return this;
	    }

	    /**
	     * Visits an interface bound of the last visited formal type parameter.
	     * 
	     * @return a non null visitor to visit the signature of the interface bound.
	     */
	    public SignatureVisitor visitInterfaceBound() {
	        separator = seenInterfaceBound ? ", " : " extends ";
	        seenInterfaceBound = true;
	        startType();
	        return this;
	    }

	    /**
	     * Visits the type of the super class.
	     * 
	     * @return a non null visitor to visit the signature of the super class
	     *         type.
	     */
	    public SignatureVisitor visitSuperclass() {
	        endFormals();
	        separator = " extends ";
	        startType();
	        return this;
	    }

	    /**
	     * Visits the type of an interface implemented by the class.
	     * 
	     * @return a non null visitor to visit the signature of the interface type.
	     */
	    public SignatureVisitor visitInterface() {
	    	if (!seenInterface) {
	    		endGenericSuperclass();
	    	}
	        separator = seenInterface ? ", " : isInterface
	                ? " extends "
	                : " implements ";
	        seenInterface = true;
	        startType();
	        return this;
	    }

	    /**
	     * Visits the type of a method parameter.
	     * 
	     * @return a non null visitor to visit the signature of the parameter type.
	     */
	    public SignatureVisitor visitParameterType() {
	        endFormals();
	        if (!seenParameter) {
	            seenParameter = true;
	            declaration.append('(');
	        } else {
	            declaration.append(", ");
	        }
	        startType();
	        return this;
	    }

	    /**
	     * Visits the return type of the method.
	     * 
	     * @return a non null visitor to visit the signature of the return type.
	     */
	    public SignatureVisitor visitReturnType() {
	        endFormals();
	        if (!seenParameter) {
	            declaration.append('(');
	        } else {
	            seenParameter = false;
	        }
	        declaration.append(')');
	        returnType = new StringBuffer();
	        return new SigVisitor(returnType);
	    }

	    /**
	     * Visits the type of a method exception.
	     * 
	     * @return a non null visitor to visit the signature of the exception type.
	     */
	    public SignatureVisitor visitExceptionType() {
	        if (exceptions == null) {
	            exceptions = new StringBuffer();
	        } else {
	            exceptions.append(", ");
	        }
	        // startType();
	        return new SigVisitor(exceptions);
	    }

	    /**
	     * Visits a signature corresponding to a primitive type.
	     * 
	     * @param descriptor the descriptor of the primitive type, or 'V' for
	     *        <tt>void</tt>.
	     */
	    public void visitBaseType(final char descriptor) {
	    	if (DEBUG) System.out.println("visitBaseType " + descriptor);
	        switch (descriptor) {
	            case 'V':
	                declaration.append("void");
	                break;
	            case 'B':
	                declaration.append("byte");
	                break;
	            case 'J':
	                declaration.append("long");
	                break;
	            case 'Z':
	                declaration.append("boolean");
	                break;
	            case 'I':
	                declaration.append("int");
	                break;
	            case 'S':
	                declaration.append("short");
	                break;
	            case 'C':
	                declaration.append("char");
	                break;
	            case 'F':
	                declaration.append("float");
	                break;
	            // case 'D':
	            default:
	                declaration.append("double");
	                break;
	        }
	        endType();
	    }

	    /**
	     * Visits a signature corresponding to a type variable.
	     * 
	     * @param name the name of the type variable.
	     */
	    public void visitTypeVariable(final String name) {
	    	if (DEBUG) System.out.println("visitTypeVariable " + name);
	    	TypeVariableImpl t = new TypeVariableImpl(name);
	    	typeVariables.add(t);
	    	stack.push(t);
	        declaration.append(name);
	        endType();
	    }

	    /**
	     * Visits a signature corresponding to an array type.
	     * 
	     * @return a non null visitor to visit the signature of the array element
	     *         type.
	     */
	    public SignatureVisitor visitArrayType() {
	        startType();
	        arrayStack |= 1;
	        return this;
	    }

	    /**
	     * Starts the visit of a signature corresponding to a class or interface
	     * type.
	     * 
	     * @param name the internal name of the class or interface.
	     */
	    public void visitClassType(final String name) {
	    	ClassTypeImpl t = new ClassTypeImpl(name.replace('/', '.'));
	    	stack.push(t);
	    	if (DEBUG) System.out.println("visitClassType " + name.replace('/', '.'));

	    	if (!"java/lang/Object".equals(name)) {
	            declaration.append(separator).append(name.replace('/', '.'));
	        } else {
	            // Map<java.lang.Object,java.util.List>
	            // or
	            // abstract public V get(Object key); (seen in Dictionary.class)
	            // should have Object
	            // but java.lang.String extends java.lang.Object is unnecessary
	            boolean needObjectClass = argumentStack % 2 != 0 || seenParameter;
	            if (needObjectClass) {
	                declaration.append(separator).append(name.replace('/', '.'));
	            }
	        }
	        separator = "";
	        argumentStack *= 2;
	        argumentStackPosition.push(stack.size());
	    }

	    /**
	     * Visits an inner class.
	     * 
	     * @param name the local name of the inner class in its enclosing class.
	     */
	    public void visitInnerClassType(final String name) {
	    	if (DEBUG) System.out.println("visitInnerClassType " + name);
	        if (argumentStack % 2 != 0) {
	            declaration.append('>');
	        }
	        argumentStack /= 2;
	        argumentStackPosition.pop();
	        declaration.append('.');
	        declaration.append(separator).append(name.replace('/', '.'));
	        separator = "";
	        argumentStack *= 2;
	        argumentStackPosition.push(stack.size());
	    }

	    /**
	     * Visits an unbounded type argument of the last visited class or inner
	     * class type.
	     */
	    public void visitTypeArgument() {
	    	if (DEBUG) System.out.println("visitTypeArgument");
	        if (argumentStack % 2 == 0) {
	            ++argumentStack;
	            declaration.append('<');
	        } else {
	            declaration.append(", ");
	        }
	        declaration.append('?');
	    }

	    /**
	     * Visits a type argument of the last visited class or inner class type.
	     * 
	     * @param wildcard '+', '-' or '='.
	     * @return a non null visitor to visit the signature of the type argument.
	     */
	    public SignatureVisitor visitTypeArgument(final char tag) {
	        if (argumentStack % 2 == 0) {
	            ++argumentStack;
	            declaration.append('<');
	        } else {
	            declaration.append(", ");
	        }

	        if (tag == SignatureVisitor.EXTENDS) {
	            declaration.append("? extends ");
	        } else if (tag == SignatureVisitor.SUPER) {
	            declaration.append("? super ");
	        }

	        startType();
	        return this;
	    }

	    /**
	     * Ends the visit of a signature corresponding to a class or interface type.
	     */
	    public void visitEnd() {
	    	if (DEBUG) System.out.println("visitEnd");
	    	if (DEBUG) System.out.println("\t" + declaration);
	        if (argumentStack % 2 != 0) {
		        int stackPos = argumentStackPosition.peek().intValue() - 1;

	        	Type raw = stack.elementAt(stackPos);
	        	Type owner = null; // FIXME
	        	
	        	int l = stack.size() - stackPos - 1;
	        	Type args[] = new Type[l];
	        	for(int i = l-1; i >= 0; i--) {
	        		args[i] = stack.pop();
	        	}
	        	stack.pop();
	        	ParameterizedTypeImpl t = new ParameterizedTypeImpl(owner, args, raw);
	        	stack.push(t);
	            declaration.append('>');
	        }
	        argumentStack /= 2;
	        argumentStackPosition.pop();
	        endType();
	    }

	    public String getDeclaration() {
	        return declaration.toString();
	    }

	    public String getReturnType() {
	        return returnType == null ? null : returnType.toString();
	    }

	    public String getExceptions() {
	        return exceptions == null ? null : exceptions.toString();
	    }

	    // -----------------------------------------------

	    private void endFormal() {
	    	Type bounds[] = stack.toArray(new Type[stack.size()]);
	    	stack.removeAllElements();
	    	formalTypeParameters.getLast().setBounds(bounds);
	    }

	    private void endGenericSuperclass() {
	    	genericSuperclass = stack.pop();
	    }

	    public void endParsing() {
	    	if (!seenInterface) {
	    		endGenericSuperclass();
	    		genericInterfaces = new Type[0];
	    	} else {
	    		genericInterfaces = stack.toArray(new Type[stack.size()]);
	    		stack.removeAllElements();
	    		for(Type t: genericInterfaces) {
	    			if (t instanceof ParameterizedTypeImpl) {
	    				ParameterizedTypeImpl p = (ParameterizedTypeImpl) t;
	    				t = p.raw;
	    			}
	    			if (t instanceof ClassTypeImpl) {
	    				ClassTypeImpl c = (ClassTypeImpl) t;
	    				c.isInterface = true;
	    			}
	    		}
	    	}
	    	currentType.setTypeParameters(typeParameters);
	    	for(TypeVariableImpl t: typeVariables) {
	    		t.setGenericDeclaration(currentType);
	    	}
	    }

	    private void endFormals() {
	        if (seenFormalParameter) {
	        	endFormal();
	            declaration.append('>');
	            seenFormalParameter = false;
	        }
			typeParameters = formalTypeParameters.toArray(new TypeVariable[formalTypeParameters.size()]);
	    }

	    private void startType() {
	        arrayStack *= 2;
	    }

	    private void endType() {
	        if (arrayStack % 2 != 0) {
	            while (arrayStack % 2 != 0) {
	            	Type t = stack.pop();
	            	stack.push(new GenericArrayTypeImpl(t));
	                arrayStack /= 2;
	                declaration.append("[]");
	            }
	        } else {
	            arrayStack /= 2;
	        }
	    }
	}
	
	public static class ClassTypeImpl implements Type, GenericDeclaration {
		private final String name;
		private boolean isInterface;
		private TypeVariable<?>[] typeParameters;
		public ClassTypeImpl(String name) {
			this.name = name;
			isInterface = false;
		}
		public ClassTypeImpl(String name, boolean isInterface) {
			this.name = name;
			this.isInterface = isInterface;
		}
		public String toString() { return (isInterface?"interface ":"class ") + name; }
		public TypeVariable<?>[] getTypeParameters() {
			return typeParameters;
		}
		public void setTypeParameters(TypeVariable<?>[] typeParameters) {
			this.typeParameters = typeParameters;
		}
	}

	public static class GenericArrayTypeImpl implements GenericArrayType {
		public GenericArrayTypeImpl(Type genericComponentType) {
			this.genericComponentType = genericComponentType;
		}
		private Type genericComponentType;
		public Type getGenericComponentType() throws TypeNotPresentException, MalformedParameterizedTypeException {
			return genericComponentType;
		}
	}

	public static class ParameterizedTypeImpl implements ParameterizedType {
		public ParameterizedTypeImpl(Type owner, Type args[], Type raw) {
			this.owner = owner;
			this.args = args;
			this.raw = raw;
		}
		private Type owner, args[], raw;

		public Type[] getActualTypeArguments() throws TypeNotPresentException, MalformedParameterizedTypeException {
			return args;
		}
		public Type getOwnerType() {
			return owner;
		}
		public Type getRawType() {
			return raw;
		}
		public String toString() {
			StringBuffer sb = new StringBuffer();
			String s = raw.toString();
			if (s.startsWith("class ")) {
				s = s.substring(6);
			} else if (s.startsWith("interface ")) {
				s = s.substring(10);
			}
			sb.append(s);
			if (args.length != 0) {
				sb.append('<');
				boolean first = true;
				for(Type t: args) {
					if (!first) {
						sb.append(", ");
					}
					first = false;
					s = t.toString();
					if (s.startsWith("class ")) {
						s = s.substring(6);
					}
					sb.append(s);
				}
				sb.append('>');
			}
			return sb.toString();
		}
	}

	public static class TypeVariableImpl<D extends GenericDeclaration> implements TypeVariable {
		private final String name;

		public TypeVariableImpl(String name) {
			this.name = name;
		}

		private Type[] bounds;
		private D genericDeclaration;

		protected void setBounds(Type[] bounds) {
			this.bounds = bounds;
		}
		public Type[] getBounds() throws TypeNotPresentException, MalformedParameterizedTypeException {
			return bounds;
		}

		protected void setGenericDeclaration(D d) {
			genericDeclaration = d;
		}
		public D getGenericDeclaration() {
			return genericDeclaration;
		}

		public String getName() {
			return name;
		}
		
		public String toString() { return name; }
	}

	public static class WildcardTypeImpl implements WildcardType {
		public Type[] getLowerBounds() throws TypeNotPresentException, MalformedParameterizedTypeException {
			throw new UnsupportedOperationException("NotImplemented");
		}

		public Type[] getUpperBounds() throws TypeNotPresentException, MalformedParameterizedTypeException {
			throw new UnsupportedOperationException("NotImplemented");
		}
	}

}
