/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.generic;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Set;
import net.ranides.assira.reflection.*;
import net.ranides.assira.reflection.impl.AClass;

/**
 * Klasa przekazująca dalej informację o klasie w genericu. Prosty przykład problemu,
 * który rozwiązuje TypeToken:
 * <blockquote><pre>
 * List&lt;T&gt; makeList(Class&lt;T&gt; clazz, int size) {
 *     return new ArrayList&lt;T&gt;(size);
 * }
 *
 * List&lt;T&gt; copyList(List&lt;T&gt; source) {
 *     List&lt;T&gt; target = makeList( ? , source.size());
 *     for(T item : source) target.add(item);
 *     return target;
 * }
 * </pre></blockquote>
 *
 * {@code makeList} pobiera instancję {@code Class<T>} bo jakoś musi dostać to nieszczęsne T.
 * Problem w tym, że copyList już takiej instancji nie dostaje - a nie da się utworzyć {@code new Class<T>()}
 * żeby jej przekazać. Oczywiście {@code new TypeToken<T>()} jest legalne. I tylko z tego
 * właśnie powodu klasa została napisana. Rozszerzony i poprawiony kod:
 *
 * <blockquote><pre>
 * List&lt;T&gt; makeList(Class&lt;T&gt; clazz, int size) {
 *     return new ArrayList&lt;T&gt;(size);
 * }
 *
 * List&lt;T&gt; makeList(TypeToken&lt;T&gt; token, int size) {
 *     return new ArrayList&lt;T&gt;(size);
 * }
 *
 * List&lt;T&gt; copyList(List&lt;T&gt; source) {
 *     List&lt;T&gt; target = makeList( new TypeToken&lt;T&gt;(){} , source.size());
 *     for(T item : source) target.add(item);
 *     return target;
 * }
 * </pre></blockquote>
 *
 * <p>
 * Uwaga. Klasa potrafi porządnie wydedukować instancję {@code Class<T>}.
 * Rozszerzenie podpatrzone z biblioteki <a href="http://wiki.fasterxml.com/JacksonHome">Jackson JSON Processor</a>
 * </p>
 * @param <T> typ, który chcemy przekazać dalej
 * 
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public abstract class TypeToken<T> extends AClass<T> {
    
    private final IClass<?> type;

    /**
     * Tworzy nową instancję klasy TypeToken. Możliwe i sensowne tylko w formie
     * utworzenia nowej klasy anonimowej, parametryzowanej typem, który chcemy
     * przekazać / przechować / wydedukować:
     * <pre>
     * {@code new TypeToken<Integer>(){ } }
     * </pre>
     *
     */
    protected TypeToken() {
        // typeinfo(class).params[0]
        // wstawiło by nam do kontekstu parametr "T" pobrany z deklaracji TypeTokena
        ParameterizedType ptype = (ParameterizedType)getClass().getGenericSuperclass();
        this.type = IClass.typeinfo(ptype.getActualTypeArguments()[0]);
    }
	
	protected TypeToken(Type type) {
		this.type = IClass.typeinfo(type);
	}
    
    protected TypeToken(IClass<?> type) {
		this.type = type;
	}
    
    @Override
    public IContext context() {
        return ((AClass)type).context();
    }

    @Override
    public IAnnotations annotations() {
        return type.annotations();
    }

    @Override
    public Set<IAttribute> attributes() {
        return type.attributes();
    }

    @Override
    public String name() {
        return type.name();
    }
    
    @Override
    public final String shortname() {
        return type.shortname();
    }

    @Override
    public boolean isInstance(Object value) {
        return type.isInstance(value);
    }

    @Override
    public Class<?> raw() {
        return type.raw();
    }

    @Override
    public Type reflective() {
        return type.reflective();
    }

    @Override
    public IClasses inner() {
        return type.inner();
    }

    @Override
    public IClass<?> outer() {
        return type.outer();
    }

    @Override
    public IClasses interfaces() {
        return type.interfaces();
    }

    @Override
    public IClasses parents() {
        return type.parents();
    }

    @Override
    public IClass<?> parent() {
        return type.parent();
    }

    @Override
    public IMethods methods() {
        return type.methods();
    }

    @Override
    public IMethods constructors() {
        return type.constructors();
    }

    @Override
    public IFields fields() {
        return type.fields();
    }

    @Override
    public List<IClass<?>> params() {
        return type.params();
    }
    
    

}
