Package org.jquantlib.lang.reflect

Source Code of org.jquantlib.lang.reflect.TypeReference

/*
Copyright (C) 2007 Richard Gomes

This source code is release under the BSD License.

This file is part of JQuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://jquantlib.org/

JQuantLib is free software: you can redistribute it and/or modify it
under the terms of the JQuantLib license.  You should have received a
copy of the license along with this program; if not, please email
<jquant-devel@lists.sourceforge.net>. The license is also available online at
<http://www.jquantlib.org/index.php/LICENSE.TXT>.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the license for more details.

JQuantLib is based on QuantLib. http://quantlib.org/
When applicable, the original copyright notice follows this notice.
*/

package org.jquantlib.lang.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import org.jquantlib.QL;

/**
* This class provides derived classes the ability to retrieve runtime type information
* from generic parametric types specified at creation time.
* <p>
* A typical use case would be an instance which is interested on a generic parameter which directs it
* which type of data can be accepted. Below you can see a typical creation use case:
* <pre>
* MyClass<String,Data> smap = new MyClass<String,Data>() {};
* MyClass<Double,Data> dmap = new MyClass<Double,Data>() {};
* </pre>
* ... and below you can see a typical retrieval of type information inside the class of our interest:
* <pre>
* class Myclass<K,V> extends TypeReference {
*
*    Class<?> final keyClass;
*    Class<?> final valClass;
*
*    MyClass() {
*        // retrieves first actual generic parameter
*        keyClass = getGenericParameterClass();
*        // retrieves second actual generic parameter
*        valClass = getGenericParameterClass(1);
*    }
*
*    public put(K key, V val) {
*        if (!keyClass.isAssignableFrom(key)) throw new ClassCastException("invalid key");
*        if (!valClass.isAssignableFrom(val)) throw new ClassCastException("invalid value");
*    }
* </pre>
*
* @note It's very important to notice that we <b>it is required</b> to create anonymous instances
*       or at least instances of an extended class of the class you are interested to parameterize.
*       The reason is that TypeToken and TypeReference uses the tricky Class.getGenericSuperClass(),
*       which retrieves type information from the super class of the current caller instance.
*
* @see TypeToken
* @see TypeReference
* @see TypeTokenTree
*
* @see <a href="http://gafter.blogspot.com/2006/12/super-type-tokens.html">Super Type Tokens</a>
* @see <a href="http://gafter.blogspot.com/2007/05/limitation-of-super-type-tokens.html">A Limitation of Super Type Tokens</a>
* @see <a href="http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf">Generics Tutorial</a>
* @see <a href="http://www.jquantlib.org/index.php/Using_TypeTokens_to_retrieve_generic_parameters">Using TypeTokens to retrieve generic parameters</a>
*
* @author Richard Gomes
*/
// TODO: code review :: license, class comments, comments for access modifiers, comments for @Override
// TODO: review convenience of this class and its methods
public abstract class TypeReference<T> {

    private final Type[] types;
    private volatile Constructor<?> constructor;

    protected TypeReference() {
        final Type superclass = getClass().getGenericSuperclass();
        if (superclass instanceof Class) {
            throw new IllegalArgumentException("Class should be anonymous or extended from a generic class");
        }
        this.types = ((ParameterizedType) superclass).getActualTypeArguments();
    }

    /**
     * Gets the referenced type of the first generic parameter
     */
    public Type getGenericType() {
        return getGenericType(0);
    }

    /**
     * Gets the referenced type of the n-th generic parameter
     */
    public Type getGenericType(final int n) {
        return this.types[n];
    }

    /**
     * Gets the referenced Class of the first generic parameter
     */
    public Class<?> getGenericParameterClass() {
        return getGenericParameterClass(0);
    }

    /**
     * Gets the referenced Class of the n-th generic parameter
     */
    public Class<?> getGenericParameterClass(final int n) {
        QL.require(n < types.length , "Missing parameter"); // QA:[RG]::verified // TODO: message
        final Type type = types[n];
        final Class<?> clazz = (type instanceof Class<?>) ? (Class<?>) type : (Class<?>) ((ParameterizedType) type).getRawType();
        QL.require(((clazz.getModifiers() & Modifier.ABSTRACT) == 0) , "generic parameter must be a concrete class"); // QA:[RG]::verified // TODO: message
        return clazz;
    }

    /**
     * Instantiates a new instance of {@code T} using the first generic parameter
     */
    @SuppressWarnings("unchecked")
    public T newGenericInstance() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        return newGenericInstance(0);
    }

    /**
     * Instantiates a new instance of {@code T} using the n-th generic parameter
     */
    @SuppressWarnings("unchecked")
    public T newGenericInstance(final int n) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        if (constructor == null) {
            constructor = getGenericParameterClass(n).getConstructor();
        }
        return (T) constructor.newInstance();
    }

    /**
     * Instantiates a new instance of {@code T} using the n-th generic parameter
     */
    @SuppressWarnings("unchecked")
    public T newGenericInstance(final int n, final Object ... objects) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        final Type type = types[n];
        final Class<?> rawType = type instanceof Class<?> ? (Class<?>) type : (Class<?>) ((ParameterizedType) type).getRawType();

        final Class<?>[] types = new Class[objects.length];
        for (int i=0; i<objects.length; i++) {
            types[i] = objects[i].getClass();
        }
        constructor = rawType.getConstructor(types);

        return (T) constructor.newInstance(objects);
    }

    /**
     * function identifies type of the typeNum -th generics in the paramNum parameter
     * @param paramNum parameter number
     * @param typeNum generics type in the paramNum parameter
     * @return Type of the
     */
    public Type getActualTypeParameters(final int paramNum, final int typeNum)
    {
        return ((ParameterizedType)getGenericType(paramNum)).getActualTypeArguments()[typeNum];
    }


    @Override
    public boolean equals(final Object o) {
        if (o instanceof TypeReference) {
            final int len = ((TypeReference)o).types.length;
            if (len!=types.length) {
                return false;
            }
            for (int i=0; i<types.length; i++) {
                if (! ((TypeReference) o).types[i].equals(this.types[i]) ) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        for (final Type type : types) {
            hash = (hash << 1) + type.hashCode();
        }
        return hash;
    }

}
TOP

Related Classes of org.jquantlib.lang.reflect.TypeReference

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.