All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.wicket.util.lang.Objects Maven / Gradle / Ivy

There is a newer version: 10.1.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.wicket.util.lang;

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;


/**
 * Object utilities.
 * 
 * @author Jonathan Locke
 */
public final class Objects
{
	/** Type tag meaning java.math.BigDecimal. */
	private static final int BIGDEC = 9;

	/** Type tag meaning java.math.BigInteger. */
	private static final int BIGINT = 6;

	/** Type tag meaning boolean. */
	private static final int BOOL = 0;

	/** Type tag meaning byte. */
	private static final int BYTE = 1;

	/** Type tag meaning char. */
	private static final int CHAR = 2;

	/** Type tag meaning double. */
	private static final int DOUBLE = 8;

	/** Type tag meaning float. */
	private static final int FLOAT = 7;

	/** Type tag meaning int. */
	private static final int INT = 4;

	/** Type tag meaning long. */
	private static final int LONG = 5;

	/**
	 * The smallest type tag that represents reals as opposed to integers. You can see whether a
	 * type tag represents reals or integers by comparing the tag to this constant: all tags less
	 * than this constant represent integers, and all tags greater than or equal to this constant
	 * represent reals. Of course, you must also check for NONNUMERIC, which means it is not a
	 * number at all.
	 */
	private static final int MIN_REAL_TYPE = FLOAT;

	/** Type tag meaning something other than a number. */
	private static final int NONNUMERIC = 10;

	/** Type tag meaning short. */
	private static final int SHORT = 3;

	/** defaults for primitives. */
	private static final HashMap, Object> primitiveDefaults = Generics.newHashMap();


	static
	{
		primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);
		primitiveDefaults.put(Byte.TYPE, (byte)0);
		primitiveDefaults.put(Short.TYPE, (short)0);
		primitiveDefaults.put(Character.TYPE, (char)0);
		primitiveDefaults.put(Integer.TYPE, 0);
		primitiveDefaults.put(Long.TYPE, 0L);
		primitiveDefaults.put(Float.TYPE, 0.0f);
		primitiveDefaults.put(Double.TYPE, 0.0);
		primitiveDefaults.put(BigInteger.class, new BigInteger("0"));
		primitiveDefaults.put(BigDecimal.class, new BigDecimal(0.0));
	}

	/**
	 * Evaluates the given object as a BigDecimal.
	 * 
	 * @param value
	 *            an object to interpret as a BigDecimal
	 * @return the BigDecimal value implied by the given object
	 * @throws NumberFormatException
	 *             if the given object can't be understood as a BigDecimal
	 */
	public static BigDecimal bigDecValue(final Object value) throws NumberFormatException
	{
		if (value == null)
		{
			return BigDecimal.valueOf(0L);
		}
		Class c = value.getClass();
		if (c == BigDecimal.class)
		{
			return (BigDecimal)value;
		}
		if (c == BigInteger.class)
		{
			return new BigDecimal((BigInteger)value);
		}
		if (c.getSuperclass() == Number.class)
		{
			return new BigDecimal(((Number)value).doubleValue());
		}
		if (c == Boolean.class)
		{
			return BigDecimal.valueOf((Boolean)value ? 1 : 0);
		}
		if (c == Character.class)
		{
			return BigDecimal.valueOf(((Character)value).charValue());
		}
		return new BigDecimal(stringValue(value, true));
	}

	/**
	 * Evaluates the given object as a BigInteger.
	 * 
	 * @param value
	 *            an object to interpret as a BigInteger
	 * @return the BigInteger value implied by the given object
	 * @throws NumberFormatException
	 *             if the given object can't be understood as a BigInteger
	 */
	public static BigInteger bigIntValue(final Object value) throws NumberFormatException
	{
		if (value == null)
		{
			return BigInteger.valueOf(0L);
		}
		Class c = value.getClass();
		if (c == BigInteger.class)
		{
			return (BigInteger)value;
		}
		if (c == BigDecimal.class)
		{
			return ((BigDecimal)value).toBigInteger();
		}
		if (c.getSuperclass() == Number.class)
		{
			return BigInteger.valueOf(((Number)value).longValue());
		}
		if (c == Boolean.class)
		{
			return BigInteger.valueOf((Boolean)value ? 1 : 0);
		}
		if (c == Character.class)
		{
			return BigInteger.valueOf((Character)value);
		}
		return new BigInteger(stringValue(value, true));
	}

	/**
	 * Evaluates the given object as a boolean: if it is a Boolean object, it's easy; if it's a
	 * Number or a Character, returns true for non-zero objects; and otherwise returns true for
	 * non-null objects.
	 * 
	 * @param value
	 *            an object to interpret as a boolean
	 * @return the boolean value implied by the given object
	 */
	public static boolean booleanValue(final Object value)
	{
		if (value == null)
		{
			return false;
		}
		Class c = value.getClass();
		if (c == Boolean.class)
		{
			return (Boolean)value;
		}
		if (c == Character.class)
		{
			return (Character)value != 0;
		}
		if (value instanceof Number)
		{
			return ((Number)value).doubleValue() != 0;
		}
		return true; // non-null
	}


	/**
	 * Compares two objects for equality, even if it has to convert one of them to the other type.
	 * If both objects are numeric they are converted to the widest type and compared. If one is
	 * non-numeric and one is numeric the non-numeric is converted to double and compared to the
	 * double numeric value. If both are non-numeric and Comparable and the types are compatible
	 * (i.e. v1 is of the same or superclass of v2's type) they are compared with
	 * Comparable.compareTo(). If both values are non-numeric and not Comparable or of incompatible
	 * classes this will throw and IllegalArgumentException.
	 * 
	 * @param v1
	 *            First value to compare
	 * @param v2
	 *            second value to compare
	 * 
	 * @return integer describing the comparison between the two objects. A negative number
	 *         indicates that v1 < v2. Positive indicates that v1 > v2. Zero indicates v1 == v2.
	 * 
	 * @throws IllegalArgumentException
	 *             if the objects are both non-numeric yet of incompatible types or do not implement
	 *             Comparable.
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static int compareWithConversion(final Object v1, final Object v2)
	{
		int result;

		if (v1 == v2)
		{
			result = 0;
		}
		else
		{
			int t1 = getNumericType(v1), t2 = getNumericType(v2), type = getNumericType(t1, t2,
				true);

			switch (type)
			{
				case BIGINT :
					result = bigIntValue(v1).compareTo(bigIntValue(v2));
					break;

				case BIGDEC :
					result = bigDecValue(v1).compareTo(bigDecValue(v2));
					break;

				case NONNUMERIC :
					if ((t1 == NONNUMERIC) && (t2 == NONNUMERIC))
					{
						if ((v1 instanceof Comparable) &&
							v1.getClass().isAssignableFrom(v2.getClass()))
						{
							result = ((Comparable)v1).compareTo(v2);
							break;
						}
						else
						{
							throw new IllegalArgumentException("invalid comparison: " +
								v1.getClass().getName() + " and " + v2.getClass().getName());
						}
					}
					// else fall through
				case FLOAT :
				case DOUBLE :
					double dv1 = doubleValue(v1),
					dv2 = doubleValue(v2);

					return (dv1 == dv2) ? 0 : ((dv1 < dv2) ? -1 : 1);

				default :
					long lv1 = longValue(v1),
					lv2 = longValue(v2);

					return (lv1 == lv2) ? 0 : ((lv1 < lv2) ? -1 : 1);
			}
		}
		return result;
	}

	/**
	 * Convert between basic Java types, i.e. primitives and their wrappers, numbers and strings.
	 * 

* This method also detects when arrays are being converted and converts the components of one * array to the type of the other. * * @param * target type * @param value * an object to be converted to the given type * @param toType * class type to be converted to * @return converted value of the type given, or null if the value cannot be converted to the * given type. */ public static T convertValue(final Object value, final Class toType) { Object result = null; if (value != null) { /* If array -> array then convert components of array individually */ if (value.getClass().isArray() && toType.isArray()) { Class componentType = toType.getComponentType(); result = Array.newInstance(componentType, Array.getLength(value)); for (int i = 0, icount = Array.getLength(value); i < icount; i++) { Array.set(result, i, convertValue(Array.get(value, i), componentType)); } } else { if ((toType == Integer.class) || (toType == Integer.TYPE)) { result = (int)longValue(value); } if ((toType == Double.class) || (toType == Double.TYPE)) { result = doubleValue(value); } if ((toType == Boolean.class) || (toType == Boolean.TYPE)) { result = booleanValue(value) ? Boolean.TRUE : Boolean.FALSE; } if ((toType == Byte.class) || (toType == Byte.TYPE)) { result = (byte)longValue(value); } if ((toType == Character.class) || (toType == Character.TYPE)) { result = (char)longValue(value); } if ((toType == Short.class) || (toType == Short.TYPE)) { result = (short)longValue(value); } if ((toType == Long.class) || (toType == Long.TYPE)) { result = longValue(value); } if ((toType == Float.class) || (toType == Float.TYPE)) { result = new Float(doubleValue(value)); } if (toType == BigInteger.class) { result = bigIntValue(value); } if (toType == BigDecimal.class) { result = bigDecValue(value); } if (toType == String.class) { result = stringValue(value); } } } else { if (toType.isPrimitive()) { result = primitiveDefaults.get(toType); } } @SuppressWarnings("unchecked") T finalResult = (T)result; return finalResult; } /** * Evaluates the given object as a double-precision floating-point number. * * @param value * an object to interpret as a double * @return the double value implied by the given object * @throws NumberFormatException * if the given object can't be understood as a double */ public static double doubleValue(final Object value) throws NumberFormatException { if (value == null) { return 0.0; } Class c = value.getClass(); if (c.getSuperclass() == Number.class) { return ((Number)value).doubleValue(); } if (c == Boolean.class) { return (Boolean)value ? 1 : 0; } if (c == Character.class) { return (Character)value; } String s = stringValue(value, true); return (s.length() == 0) ? 0.0 : Double.parseDouble(s); } /** * Returns true if a and b are equal. Either object may be null. * * @param a * Object a * @param b * Object b * @return True if the objects are equal */ public static boolean equal(final Object a, final Object b) { if (a == b) { return true; } if ((a != null) && (b != null) && a.equals(b)) { return true; } return false; } /** * Returns the constant from the NumericTypes interface that best expresses the type of an * operation, which can be either numeric or not, on the two given types. * * @param t1 * type of one argument to an operator * @param t2 * type of the other argument * @param canBeNonNumeric * whether the operator can be interpreted as non-numeric * @return the appropriate constant from the NumericTypes interface */ public static int getNumericType(int t1, int t2, final boolean canBeNonNumeric) { if (t1 == t2) { return t1; } if (canBeNonNumeric && ((t1 == NONNUMERIC) || (t2 == NONNUMERIC) || (t1 == CHAR) || (t2 == CHAR))) { return NONNUMERIC; } if (t1 == NONNUMERIC) { t1 = DOUBLE; // Try to interpret strings as doubles... } if (t2 == NONNUMERIC) { t2 = DOUBLE; // Try to interpret strings as doubles... } if (t1 >= MIN_REAL_TYPE) { if (t2 >= MIN_REAL_TYPE) { return Math.max(t1, t2); } if (t2 < INT) { return t1; } if (t2 == BIGINT) { return BIGDEC; } return Math.max(DOUBLE, t1); } else if (t2 >= MIN_REAL_TYPE) { if (t1 < INT) { return t2; } if (t1 == BIGINT) { return BIGDEC; } return Math.max(DOUBLE, t2); } else { return Math.max(t1, t2); } } /** * Returns a constant from the NumericTypes interface that represents the numeric type of the * given object. * * @param value * an object that needs to be interpreted as a number * @return the appropriate constant from the NumericTypes interface */ public static int getNumericType(final Object value) { if (value != null) { Class c = value.getClass(); if (c == Integer.class) { return INT; } if (c == Double.class) { return DOUBLE; } if (c == Boolean.class) { return BOOL; } if (c == Byte.class) { return BYTE; } if (c == Character.class) { return CHAR; } if (c == Short.class) { return SHORT; } if (c == Long.class) { return LONG; } if (c == Float.class) { return FLOAT; } if (c == BigInteger.class) { return BIGINT; } if (c == BigDecimal.class) { return BIGDEC; } } return NONNUMERIC; } /** * Returns the constant from the NumericTypes interface that best expresses the type of a * numeric operation on the two given objects. * * @param v1 * one argument to a numeric operator * @param v2 * the other argument * @return the appropriate constant from the NumericTypes interface */ public static int getNumericType(final Object v1, final Object v2) { return getNumericType(v1, v2, false); } /** * Returns the constant from the NumericTypes interface that best expresses the type of an * operation, which can be either numeric or not, on the two given objects. * * @param v1 * one argument to an operator * @param v2 * the other argument * @param canBeNonNumeric * whether the operator can be interpreted as non-numeric * @return the appropriate constant from the NumericTypes interface */ public static int getNumericType(final Object v1, final Object v2, final boolean canBeNonNumeric) { return getNumericType(getNumericType(v1), getNumericType(v2), canBeNonNumeric); } /** * Returns true if object1 is equal to object2 in either the sense that they are the same object * or, if both are non-null if they are equal in the equals() sense. * * @param object1 * First object to compare * @param object2 * Second object to compare * * @return true if v1 == v2 */ public static boolean isEqual(final Object object1, final Object object2) { boolean result = false; if (object1 == object2) { result = true; } else { if ((object1 != null) && object1.getClass().isArray()) { if ((object2 != null) && object2.getClass().isArray() && (object2.getClass() == object1.getClass())) { result = (Array.getLength(object1) == Array.getLength(object2)); if (result) { for (int i = 0, icount = Array.getLength(object1); result && (i < icount); i++) { result = isEqual(Array.get(object1, i), Array.get(object2, i)); } } } } else { // Check for converted equivalence first, then equals() // equivalence result = (object1 != null) && (object2 != null) && ((compareWithConversion(object1, object2) == 0) || object1.equals(object2)); } } return result; } /** * Evaluates the given object as a long integer. * * @param value * an object to interpret as a long integer * @return the long integer value implied by the given object * @throws NumberFormatException * if the given object can't be understood as a long integer */ public static long longValue(final Object value) throws NumberFormatException { if (value == null) { return 0L; } Class c = value.getClass(); if (c.getSuperclass() == Number.class) { return ((Number)value).longValue(); } if (c == Boolean.class) { return (Boolean)value ? 1 : 0; } if (c == Character.class) { return (Character)value; } return Long.parseLong(stringValue(value, true)); } /** * Returns a new Number object of an appropriate type to hold the given integer value. The type * of the returned object is consistent with the given type argument, which is a constant from * the NumericTypes interface. * * @param type * the nominal numeric type of the result, a constant from the NumericTypes interface * @param value * the integer value to convert to a Number object * @return a Number object with the given value, of type implied by the type argument */ public static Number newInteger(final int type, final long value) { switch (type) { case BOOL : case CHAR : case INT : return (int)value; case FLOAT : return (float)value; case DOUBLE : return (double)value; case LONG : return value; case BYTE : return (byte)value; case SHORT : return (short)value; default : return BigInteger.valueOf(value); } } /** * Evaluates the given object as a String. * * @param value * an object to interpret as a String * @return the String value implied by the given object as returned by the toString() method, or * "null" if the object is null. */ public static String stringValue(final Object value) { return stringValue(value, false); } /** * returns hashcode of the objects by calling obj.hashcode(). safe to use when obj is null. * * @param obj * @return hashcode of the object or 0 if obj is null */ public static int hashCode(final Object... obj) { if ((obj == null) || (obj.length == 0)) { return 0; } int result = 37; for (int i = obj.length - 1; i > -1; i--) { result = 37 * result + (obj[i] != null ? obj[i].hashCode() : 0); } return result; } /** * Evaluates the given object as a String and trims it if the trim flag is true. * * @param value * an object to interpret as a String * @param trim * whether to trim the string * @return the String value implied by the given object as returned by the toString() method, or * "null" if the object is null. */ public static String stringValue(final Object value, final boolean trim) { String result; if (value == null) { result = "null"; } else { result = value.toString(); if (trim) { result = result.trim(); } } return result; } /** * Returns the original object if this one is != null. If the original object is null * the default one is returned. The default object has no restriction, it might be itself null. * * @param originalObj * the original object * @param defaultObj * the default object * @return the original object if not null, the default one otherwise. */ public static T defaultIfNull(T originalObj, T defaultObj) { return originalObj != null ? originalObj : defaultObj; } /** * Instantiation not allowed */ private Objects() { } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy