// $Id: TypeLib.java 271 2009-06-27 22:42:27Z revilo $
/*
 Copyright (c) 2006, 2007, The Cytoscape Consortium (www.cytoscape.org)

 The Cytoscape Consortium is:
 - Institute for Systems Biology
 - University of California San Diego
 - Memorial Sloan-Kettering Cancer Center
 - Institut Pasteur
 - Agilent Technologies

 This library is free software; you can redistribute it and/or modify it
 under the terms of the GNU Lesser General Public License as published
 by the Free Software Foundation; either version 2.1 of the License, or
 any later version.

 This library 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.  The software and
 documentation provided hereunder is on an "as is" basis, and the
 Institute for Systems Biology and the Whitehead Institute
 have no obligations to provide maintenance, support,
 updates, enhancements or modifications.  In no event shall the
 Institute for Systems Biology and the Whitehead Institute
 be liable to any party for direct, indirect, special,
 incidental or consequential damages, including lost profits, arising
 out of the use of this software and its documentation, even if the
 Institute for Systems Biology and the Whitehead Institute
 have been advised of the possibility of such damage.  See
 the GNU Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public License
 along with this library; if not, write to the Free Software Foundation,
 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/

package prefuse.util;


/**
 * Library routines dealing with Java Class types.
 *
 * @author <a href="http://jheer.org">jeffrey heer</a>
 */
public class TypeLib {
	private TypeLib() {
		// prevent instantiation
	}

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

	/**
	 * Check if an object is an instance of a given class, or, if the class
	 * is a primitive type, if the Object is an instance of the wrapper class
	 * for that primitive (e.g., as Integer is a wrapper for int).
	 * @param type the Class type
	 * @param instance the Object instance
	 * @return true if the object is an instance of the given class, or if
	 * of the appropriate primitive wrapper type.
	 */
	public static boolean typeCheck(Class type, Object instance) {
		return type.isAssignableFrom(instance.getClass()) || isWrapperInstance(type, instance);
	}

	/**
	 * Get the nearest shared ancestor class of two objects. Note: this
	 * currently does not compute the actual least common ancestor, but
	 * only looks up one level in the inheritance tree and quits if
	 * it does not find a match.
	 * @param o1 the first object
	 * @param o2 the second object
	 * @return the nearest class instance of which both objects
	 * are instances
	 */
	public static Class getSharedType(Object o1, Object o2) {
		return getSharedType(o1.getClass(), o2.getClass());
	}

	/**
	 * Get the nearest shared ancestor class of two classes. Note: this
	 * currently does not compute the actual least common ancestor, but
	 * only looks up one level in the inheritance tree and quits if
	 * it does not find a match.
	 * @param type1 the first type
	 * @param type2 the second type
	 * @return the nearest class instance which is equal to or a
	 * superclass of the two class instances
	 */
	public static Class getSharedType(Class type1, Class type2) {
		if (type1 == type2) {
			return type1;
		} else if (type1.isAssignableFrom(type2)) {
			return type1;
		} else if (type2.isAssignableFrom(type1)) {
			return type2;
		} else {
			return null;
		}
	}

	/**
	 * Indicates if an object is an instance of a wrapper class for a given
	 * primitive type.
	 * @param type the primitive Class type
	 * @param instance the object to test as wrapper (e.g., as Integer is a
	 * wrapper type for int)
	 * @return true if the object is a wrapper instance of the given
	 * primitive type
	 */
	public static boolean isWrapperInstance(Class type, Object instance) {
		if (!type.isPrimitive())
			throw new IllegalArgumentException("Input type must be a primitive");

		if ((int.class == type) && instance instanceof Integer) {
			return true;
		} else if ((long.class == type) && instance instanceof Long) {
			return true;
		} else if ((float.class == type) && instance instanceof Float) {
			return true;
		} else if ((double.class == type) && instance instanceof Double) {
			return true;
		} else if ((boolean.class == type) && instance instanceof Boolean) {
			return true;
		} else if ((short.class == type) && instance instanceof Short) {
			return true;
		} else if ((byte.class == type) && instance instanceof Byte) {
			return true;
		} else if ((char.class == type) && instance instanceof Character) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Given a numeric (byte, short, int, long, float, or double) class type or
	 * associated wrapper class type, return the primitive class type
	 * @param type the type to look up, must be a numerical type, but can be
	 * either primitive or a wrapper.
	 * @return the primitive class type
	 */
	public static Class getPrimitiveType(Class type) {
		if (Integer.class.equals(type) || (type == int.class)) {
			return int.class;
		} else if (Long.class.equals(type) || (type == long.class)) {
			return long.class;
		} else if (Float.class.equals(type) || (type == float.class)) {
			return float.class;
		} else if (Double.class.equals(type) || (type == double.class)) {
			return double.class;
		} else if (Byte.class.equals(type) || (type == byte.class)) {
			return byte.class;
		} else if (Short.class.equals(type) || (type == short.class)) {
			return short.class;
		} else {
			throw new IllegalArgumentException("Input class must be a numeric type");
		}
	}

	/**
	 * Get the wrapper class type for a primitive class type.
	 * @param type a class type
	 * @return the wrapper class for the input type if it is a
	 * primitive class type, otherwise returns the input type
	 */
	public static Class getWrapperType(Class type) {
		if (!type.isPrimitive()) {
			return type;
		} else if (int.class == type) {
			return Integer.class;
		} else if (long.class == type) {
			return Long.class;
		} else if (float.class == type) {
			return Float.class;
		} else if (double.class == type) {
			return Double.class;
		} else if (boolean.class == type) {
			return Boolean.class;
		} else if (short.class == type) {
			return Short.class;
		} else if (char.class == type) {
			return Character.class;
		} else if (byte.class == type) {
			return Byte.class;
		} else if (short.class == type) {
			return Short.class;
		} else {
			throw new IllegalArgumentException();
		}
	}

	/**
	 * Indicates if a given class type is a primitive integer type
	 * (one of byte, short, int, or long).
	 * @param type the type to check
	 * @return true if it is a primitive numeric type, false otherwise
	 */
	public static boolean isIntegerType(Class type) {
		return ((type == byte.class) || (type == short.class) || (type == int.class)
		       || (type == long.class));
	}

	/**
	 * Indicates if a given class type is a primitive numeric one type
	 * (one of byte, short, int, long, float, or double).
	 * @param type the type to check
	 * @return true if it is a primitive numeric type, false otherwise
	 */
	public static boolean isNumericType(Class type) {
		return ((type == byte.class) || (type == short.class) || (type == int.class)
		       || (type == long.class) || (type == double.class) || (type == float.class));
	}

	/**
	 * Get a compatible numeric type for two primitive numeric
	 * class types. Any of (byte, short, int) will resolve to int.
	 * @param c1 a numeric primitive class type (int, long, float, or double)
	 * @param c2 a numeric primitive class type (int, long, float, or double)
	 * @return the compatible numeric type for binary operations involving
	 * both types.
	 */
	public static Class getNumericType(Class c1, Class c2) {
		if (!isNumericType(c1) || !isNumericType(c2)) {
			throw new IllegalArgumentException("Input types must be primitive number types");
		}

		if ((c1 == double.class) || (c2 == double.class)) {
			return double.class;
		} else if ((c1 == float.class) || (c1 == float.class)) {
			return float.class;
		} else if ((c1 == long.class) || (c2 == long.class)) {
			return long.class;
		} else {
			return int.class;
		}
	}
} // end of class TypeLib
