Contains static methods related to Java and JDO type conversions
/* * 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 <a href="mailto:jackknifebarber@users.sourceforge.net">Mike Martin</a> * @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); } }