Java/J2EE/JDO
/*
* Copyright 2005 (C) TJDO.
* All rights reserved.
*
* This software is distributed under the terms of the TJDO License version 1.0.
* See the terms of the TJDO License in the documentation provided with this software.
*
* $Id: Types.java,v 1.1 2005/12/13 20:46:16 jackknifebarber Exp $
*/
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;
/**
* Contains static methods related to Java and JDO type conversions.
*
* @author
* @version $Revision: 1.1 $
*/
public final class Types
{
private Types()
{
}
private static final HashMap WRAPPERS_BY_PRIMITIVE = new HashMap(9);
private static final HashMap PRIMITIVES_BY_WRAPPER = new HashMap(9);
static
{
WRAPPERS_BY_PRIMITIVE.put(Boolean.TYPE, Boolean.class);
WRAPPERS_BY_PRIMITIVE.put(Byte.TYPE, Byte.class);
WRAPPERS_BY_PRIMITIVE.put(Character.TYPE, Character.class);
WRAPPERS_BY_PRIMITIVE.put(Short.TYPE, Short.class);
WRAPPERS_BY_PRIMITIVE.put(Integer.TYPE, Integer.class);
WRAPPERS_BY_PRIMITIVE.put(Long.TYPE, Long.class);
WRAPPERS_BY_PRIMITIVE.put(Float.TYPE, Float.class);
WRAPPERS_BY_PRIMITIVE.put(Double.TYPE, Double.class);
WRAPPERS_BY_PRIMITIVE.put(Void.TYPE, Void.class);
PRIMITIVES_BY_WRAPPER.put(Boolean.class, Boolean.TYPE);
PRIMITIVES_BY_WRAPPER.put(Byte.class, Byte.TYPE);
PRIMITIVES_BY_WRAPPER.put(Character.class, Character.TYPE);
PRIMITIVES_BY_WRAPPER.put(Short.class, Short.TYPE);
PRIMITIVES_BY_WRAPPER.put(Integer.class, Integer.TYPE);
PRIMITIVES_BY_WRAPPER.put(Long.class, Long.TYPE);
PRIMITIVES_BY_WRAPPER.put(Float.class, Float.TYPE);
PRIMITIVES_BY_WRAPPER.put(Double.class, Double.TYPE);
PRIMITIVES_BY_WRAPPER.put(Void.class, Void.TYPE);
}
/**
* Performs a boxing conversion, if applicable.
* If the type is a Java primitive type it"s equivalent wrapper class is
* returned.
* Otherwise the type itself is returned.
*
* @param c
* the type to be boxed, or <code>null</code>
*
* @return
* The boxed equivalent, or the type itself if not applicable.
* <code>null</code> is returned if the argument was null.
*/
public static Class box(Class c)
{
Class boxed = (Class)WRAPPERS_BY_PRIMITIVE.get(c);
return boxed == null ? c : boxed;
}
/**
* Performs an unboxing conversion, if applicable.
* If the type is a Java primitive wrapper class it"s equivalent primitive
* type is returned.
* Otherwise the type itself is returned.
*
* @param c
* the type to be unboxed, or <code>null</code>
*
* @return
* The unboxed equivalent, or the type itself if not applicable.
* <code>null</code> is returned if the argument was null.
*/
public static Class unbox(Class c)
{
Class unboxed = (Class)PRIMITIVES_BY_WRAPPER.get(c);
return unboxed == null ? c : unboxed;
}
/**
* Performs a boxing conversion on multiple classes.
* The returned array contains the results of calling {@link #box(Class)}
* on every class in the argument array.
*
* @param c
* the types to be boxed
*
* @return
* the boxed equivalent of all the types in <var>c</var>
*/
public static Class[] box(Class[] c)
{
int n = c.length;
Class[] boxed = new Class[n];
for (int i = 0; i < n; ++i)
boxed[i] = box(c[i]);
return boxed;
}
/**
* Performs an unboxing conversion on multiple classes.
* The returned array contains the results of calling {@link #unbox(Class)}
* on every class in the argument array.
*
* @param c
* the types to be unboxed
*
* @return
* the unboxed equivalent of all the types in <var>c</var>
*/
public static Class[] unbox(Class[] c)
{
int n = c.length;
Class[] unboxed = new Class[n];
for (int i = 0; i < n; ++i)
unboxed[i] = unbox(c[i]);
return unboxed;
}
/**
* Tests if a set of parameters types are all assignable from a given set of
* argument types.
* Returns true iff the number of types match and each parameter type is
* assignable from its corresponding argument type.
* "Assignable" is defined by <code>Class.isAssignableFrom()</code>.
* <p>
* All classes in the parameter type array and the argument type array must
* already be boxed.
*
* @param boxedParamTypes
* the parameter types
* @param boxedArgTypes
* the argument types
*
* @return
* <code>true</code> if the all the parameter types are assignable from
* the argument types.
*/
public static boolean areAssignableFrom(Class[] boxedParamTypes, Class[] boxedArgTypes)
{
int n = boxedParamTypes.length;
if (n != boxedArgTypes.length)
return false;
for (int i = 0; i < n; ++i)
{
if (!boxedParamTypes[i].isAssignableFrom(boxedArgTypes[i]))
return false;
}
return true;
}
/**
* Performs <dfn>unary numeric promotion</dfn> on the given type.
* Unary numeric promotion is as defined in §5.6.1 of the Java Language
* Specification.
*
* @param operandType
* the type to be promoted, or <code>null</code>
*
* @return
* The promoted type, or <code>null</code> if the argument was null.
*/
public static Class unaryNumericPromotion(Class operandType)
{
operandType = box(operandType);
if (operandType == Byte.class || operandType == Character.class || operandType == Short.class)
return Integer.class;
else
return operandType;
}
/**
* Performs <dfn>binary numeric promotion</dfn> on a set of input types.
* Binary numeric promotion is as defined in §14.6.2 of the JDO 2.0
* spec.
*
* @param types
* The input types to be promoted. If more than two types are present
* then the first two are promoted, then the promoted type and the
* third type are promoted, and so on for all the types.
*
* @return
* The promoted type. Returns <code>null</code> if the input array
* is empty. Returns the first type if the input array has only one
* type.
*
* @exception IllegalArgumentException
* if any argument type is not a subclass of java.lang.Number
*/
public static Class binaryNumericPromotion(Class[] types)
{
Class promoted = null;
int n = types.length;
if (n > 0)
promoted = types[0];
for (int i = 1; i < n; ++i)
promoted = binaryNumericPromotion(promoted, types[i]);
return promoted;
}
/**
* Performs <dfn>binary numeric promotion</dfn> on a pair of types.
* Binary numeric promotion is as defined in §14.6.2 of the JDO 2.0
* spec.
*
* @param operand1Type
* the first type in the pair.
* @param operand2Type
* the other type in the pair.
*
* @return
* The promoted type.
*
* @exception IllegalArgumentException
* if either argument type is not a subclass of java.lang.Number
*/
public static Class binaryNumericPromotion(Class operand1Type, Class operand2Type)
{
operand1Type = box(operand1Type);
operand2Type = box(operand2Type);
if (operand1Type == BigDecimal.class || operand2Type == BigDecimal.class)
return BigDecimal.class;
if (isBigIntegerVsFloating(operand1Type, operand2Type) || isBigIntegerVsFloating(operand2Type, operand1Type))
return BigDecimal.class;
if (operand1Type == BigInteger.class || operand2Type == BigInteger.class)
return BigInteger.class;
if (operand1Type == Double.class || operand2Type == Double.class)
return Double.class;
if (operand1Type == Float.class || operand2Type == Float.class)
return Float.class;
if (operand1Type == Long.class || operand2Type == Long.class)
return Long.class;
if (!Number.class.isAssignableFrom(operand1Type) || !Number.class.isAssignableFrom(operand2Type))
throw new IllegalArgumentException("No defined numeric promotion for operands of type " + operand1Type.getName() + " and " + operand2Type.getName());
return Integer.class;
}
private static boolean isBigIntegerVsFloating(Class c1, Class c2)
{
return c1 == BigInteger.class && (c2 == Float.class || c2 == Double.class);
}
}