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

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

Go to download

Pax Wicket Service is an OSGi extension of the Wicket framework, allowing for dynamic loading and unloading of Wicket components and pageSources.

There is a newer version: 5.0.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.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;

import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.application.IClassResolver;
import org.apache.wicket.settings.IApplicationSettings;
import org.apache.wicket.util.io.ByteCountingOutputStream;
import org.apache.wicket.util.io.IObjectStreamFactory;
import org.apache.wicket.util.io.IObjectStreamFactory.DefaultObjectStreamFactory;
import org.apache.wicket.util.string.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * Object utilities.
 * 
 * @author Jonathan Locke
 */
public final class Objects
{
	/**
	 * Interface that enables users to plugin the way object sizes are calculated with Wicket.
	 */
	public static interface IObjectSizeOfStrategy
	{
		/**
		 * Computes the size of an object. This typically is an estimation, not an absolute accurate
		 * size.
		 * 
		 * @param object
		 *            Object to compute size of
		 * @return The size of the object in bytes.
		 */
		long sizeOf(Object object);
	}

	/**
	 * {@link IObjectSizeOfStrategy} that works by serializing the object to an instance of
	 * {@link ByteCountingOutputStream}, which records the number of bytes written to it. Hence,
	 * this gives the size of the object as it would be serialized,including all the overhead of
	 * writing class headers etc. Not very accurate (the real memory consumption should be lower)
	 * but the best we can do in a cheap way pre JDK 5.
	 */
	public static final class SerializingObjectSizeOfStrategy implements IObjectSizeOfStrategy
	{
		/**
		 * @see org.apache.wicket.util.lang.Objects.IObjectSizeOfStrategy#sizeOf(java.lang.Object)
		 */
		public long sizeOf(Object object)
		{
			if (object == null)
			{
				return 0;
			}
			try
			{
				final ByteCountingOutputStream out = new ByteCountingOutputStream();
				new ObjectOutputStream(out).writeObject(object);
				out.close();
				return out.size();
			}
			catch (IOException e)
			{
				if (log.isWarnEnabled())
				{
					log.warn("Unable to determine object size: " + object.toString(), e);
				}
				return -1;
			}
		}

	}

	private static final class ReplaceObjectInputStream extends ObjectInputStream
	{
		private final ClassLoader classloader;
		private final HashMap replacedComponents;

		private ReplaceObjectInputStream(InputStream in,
			HashMap replacedComponents, ClassLoader classloader)
			throws IOException
		{
			super(in);
			this.replacedComponents = replacedComponents;
			this.classloader = classloader;
			enableResolveObject(true);
		}

		// This override is required to resolve classes inside in different
		// bundle, i.e.
		// The classes can be resolved by OSGI classresolver implementation
		@Override
		protected Class resolveClass(ObjectStreamClass desc) throws IOException,
			ClassNotFoundException
		{
			String className = desc.getName();

			try
			{
				return Class.forName(className, true, classloader);
			}
			catch (ClassNotFoundException ex1)
			{
				// ignore this exception.
				log.debug("Class not found by using objects own classloader, trying the IClassResolver");
			}

			Application application = Application.get();
			IApplicationSettings applicationSettings = application.getApplicationSettings();
			IClassResolver classResolver = applicationSettings.getClassResolver();

			Class candidate = null;
			try
			{
				candidate = classResolver.resolveClass(className);
				if (candidate == null)
				{
					candidate = super.resolveClass(desc);
				}
			}
			catch (WicketRuntimeException ex)
			{
				if (ex.getCause() instanceof ClassNotFoundException)
				{
					throw (ClassNotFoundException)ex.getCause();
				}
			}
			return candidate;
		}

		@Override
		protected Object resolveObject(Object obj) throws IOException
		{
			Object replaced = replacedComponents.get(obj);
			if (replaced != null)
			{
				return replaced;
			}
			return super.resolveObject(obj);
		}
	}

	private static final class ReplaceObjectOutputStream extends ObjectOutputStream
	{
		private final HashMap replacedComponents;

		private ReplaceObjectOutputStream(OutputStream out,
			HashMap replacedComponents) throws IOException
		{
			super(out);
			this.replacedComponents = replacedComponents;
			enableReplaceObject(true);
		}

		@Override
		protected Object replaceObject(Object obj) throws IOException
		{
			if (obj instanceof Component)
			{
				final Component component = (Component)obj;
				String name = component.getPath();
				replacedComponents.put(name, component);
				return name;
			}
			return super.replaceObject(obj);
		}
	}

	/** 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;

	/** log. */
	private static final Logger log = LoggerFactory.getLogger(Objects.class);

	/** 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();

	/**
	 * The default object stream factory to use. Keep this as a static here opposed to in
	 * Application, as the Application most likely isn't available in the threads we'll be using
	 * this with.
	 */
	private static IObjectStreamFactory objectStreamFactory = new IObjectStreamFactory.DefaultObjectStreamFactory();

	/**
	 * Strategy for calculating sizes of objects. Note: I didn't make this an application setting as
	 * we have enough of those already, and the typical way this probably would be used is that
	 * install a different one according to the JDK version used, so varying them between
	 * applications doesn't make a lot of sense.
	 */
	private static IObjectSizeOfStrategy objectSizeOfStrategy = new SerializingObjectSizeOfStrategy();

	static
	{
		primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);
		primitiveDefaults.put(Byte.TYPE, new Byte((byte)0));
		primitiveDefaults.put(Short.TYPE, new Short((short)0));
		primitiveDefaults.put(Character.TYPE, new Character((char)0));
		primitiveDefaults.put(Integer.TYPE, new Integer(0));
		primitiveDefaults.put(Long.TYPE, new Long(0L));
		primitiveDefaults.put(Float.TYPE, new Float(0.0f));
		primitiveDefaults.put(Double.TYPE, new Double(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(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).booleanValue() ? 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(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).booleanValue() ? 1 : 0);
		}
		if (c == Character.class)
		{
			return BigInteger.valueOf(((Character)value).charValue());
		}
		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(Object value)
	{
		if (value == null)
		{
			return false;
		}
		Class c = value.getClass();
		if (c == Boolean.class)
		{
			return ((Boolean)value).booleanValue();
		}
		if (c == Character.class)
		{
			return ((Character)value).charValue() != 0;
		}
		if (value instanceof Number)
		{
			return ((Number)value).doubleValue() != 0;
		}
		return true; // non-null
	}

	/**
	 * De-serializes an object from a byte array.
	 * 
	 * @param data
	 *            The serialized object
	 * @return The object
	 */
	public static Object byteArrayToObject(final byte[] data)
	{
		try
		{
			final ByteArrayInputStream in = new ByteArrayInputStream(data);
			ObjectInputStream ois = null;
			boolean unsetApplication = false;
			try
			{
				ois = objectStreamFactory.newObjectInputStream(in);
				String applicationName = (String)ois.readObject();
				if (applicationName != null && !Application.exists())
				{
					Application app = Application.get(applicationName);
					if (app != null)
					{
						Application.set(app);
						unsetApplication = true;
					}
				}
				return ois.readObject();
			}
			finally
			{
				if (unsetApplication)
				{
					Application.unset();
				}
				if (ois != null)
				{
					ois.close();
				}
				in.close();
			}
		}
		catch (ClassNotFoundException e)
		{
			throw new RuntimeException("Could not deserialize object using `" +
				objectStreamFactory.getClass().getName() + "` object factory", e);
		}
		catch (IOException e)
		{
			throw new RuntimeException("Could not deserialize object using `" +
				objectStreamFactory.getClass().getName() + "` object factory", e);
		}
	}

	/**
	 * Makes a deep clone of an object by serializing and deserializing it. The object must be fully
	 * serializable to be cloned. This method will not clone wicket Components, it will just reuse
	 * those instances so that the complete component tree is not copied over only the model data.
	 * 
	 * @param object
	 *            The object to clone
	 * @return A deep copy of the object
	 */
	public static Object cloneModel(final Object object)
	{
		if (object == null)
		{
			return null;
		}
		else
		{
			try
			{
				final ByteArrayOutputStream out = new ByteArrayOutputStream(256);
				final HashMap replacedObjects = Generics.newHashMap();
				ObjectOutputStream oos = new ReplaceObjectOutputStream(out, replacedObjects);
				oos.writeObject(object);
				ObjectInputStream ois = new ReplaceObjectInputStream(new ByteArrayInputStream(
					out.toByteArray()), replacedObjects, object.getClass().getClassLoader());
				return ois.readObject();
			}
			catch (ClassNotFoundException e)
			{
				throw new WicketRuntimeException("Internal error cloning object", e);
			}
			catch (IOException e)
			{
				throw new WicketRuntimeException("Internal error cloning object", e);
			}
		}
	}

	/**
	 * Makes a deep clone of an object by serializing and deserializing it. The object must be fully
	 * serializable to be cloned. No extra debug info is gathered.
	 * 
	 * @param object
	 *            The object to clone
	 * @return A deep copy of the object
	 * @see #cloneModel(Object)
	 */
	public static Object cloneObject(final Object object)
	{
		if (object == null)
		{
			return null;
		}
		else
		{
			try
			{
				final ByteArrayOutputStream out = new ByteArrayOutputStream(256);
				ObjectOutputStream oos = new ObjectOutputStream(out);
				oos.writeObject(object);
				ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(
					out.toByteArray()))
				{
					// This override is required to resolve classes inside in different bundle, i.e.
					// The classes can be resolved by OSGI classresolver implementation
					@Override
					protected Class resolveClass(ObjectStreamClass desc) throws IOException,
						ClassNotFoundException
					{
						String className = desc.getName();

						try
						{
							return Class.forName(className, true, object.getClass()
								.getClassLoader());
						}
						catch (ClassNotFoundException ex1)
						{
							// ignore this exception.
							log.debug("Class not found by using objects own classloader, trying the IClassResolver");
						}


						Application application = Application.get();
						IApplicationSettings applicationSettings = application.getApplicationSettings();
						IClassResolver classResolver = applicationSettings.getClassResolver();

						Class candidate = null;
						try
						{
							candidate = classResolver.resolveClass(className);
							if (candidate == null)
							{
								candidate = super.resolveClass(desc);
							}
						}
						catch (WicketRuntimeException ex)
						{
							if (ex.getCause() instanceof ClassNotFoundException)
							{
								throw (ClassNotFoundException)ex.getCause();
							}
						}
						return candidate;
					}
				};
				return ois.readObject();
			}
			catch (ClassNotFoundException e)
			{
				throw new WicketRuntimeException("Internal error cloning object", e);
			}
			catch (IOException e)
			{
				throw new WicketRuntimeException("Internal error cloning object", e);
			}
		}
	}

	/**
	 * 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")
	public static int compareWithConversion(Object v1, 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;
	}

	/**
	 * Returns the value converted numerically to the given class type
	 * 
	 * This method also detects when arrays are being converted and converts the components of one
	 * array to the type of the other.
	 * 
	 * @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 value if the value cannot be converted to the
	 *         given type.
	 */
	public static Object 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 = new Integer((int)longValue(value));
				}
				if ((toType == Double.class) || (toType == Double.TYPE))
				{
					result = new Double(doubleValue(value));
				}
				if ((toType == Boolean.class) || (toType == Boolean.TYPE))
				{
					result = booleanValue(value) ? Boolean.TRUE : Boolean.FALSE;
				}
				if ((toType == Byte.class) || (toType == Byte.TYPE))
				{
					result = new Byte((byte)longValue(value));
				}
				if ((toType == Character.class) || (toType == Character.TYPE))
				{
					result = new Character((char)longValue(value));
				}
				if ((toType == Short.class) || (toType == Short.TYPE))
				{
					result = new Short((short)longValue(value));
				}
				if ((toType == Long.class) || (toType == Long.TYPE))
				{
					result = new Long(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);
			}
		}
		return (result != null) ? result : value;
	}

	/**
	 * 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(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).booleanValue() ? 1 : 0;
		}
		if (c == Character.class)
		{
			return ((Character)value).charValue();
		}
		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, 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(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(Object v1, 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(Object v1, Object v2, 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(Object object1, 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(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).booleanValue() ? 1 : 0;
		}
		if (c == Character.class)
		{
			return ((Character)value).charValue();
		}
		return Long.parseLong(stringValue(value, true));
	}

	/**
	 * Creates a new instance using the current application's class resolver. Returns null if
	 * className is null.
	 * 
	 * @param className
	 *            The full class name
	 * @return The new object instance
	 */
	public static Object newInstance(final String className)
	{
		if (!Strings.isEmpty(className))
		{
			try
			{
				Class c = Classes.resolveClass(className);
				if (c == null)
				{
					throw new WicketRuntimeException("Unable to create " + className);
				}
				return c.newInstance();
			}
			catch (ClassCastException e)
			{
				throw new WicketRuntimeException("Unable to create " + className, e);
			}
			catch (InstantiationException e)
			{
				throw new WicketRuntimeException("Unable to create " + className, e);
			}
			catch (IllegalAccessException e)
			{
				throw new WicketRuntimeException("Unable to create " + className, e);
			}
		}
		return null;
	}

	/**
	 * 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(int type, long value)
	{
		switch (type)
		{
			case BOOL :
			case CHAR :
			case INT :
				return new Integer((int)value);

			case FLOAT :
				if (value == value)
				{
					return new Float(value);
				}
				// else fall through:
			case DOUBLE :
				if (value == value)
				{
					return new Double(value);
				}
				// else fall through:
			case LONG :
				return new Long(value);

			case BYTE :
				return new Byte((byte)value);

			case SHORT :
				return new Short((short)value);

			default :
				return BigInteger.valueOf(value);
		}
	}

	/**
	 * Serializes an object into a byte array.
	 * 
	 * @param object
	 *            The object
	 * @return The serialized object
	 */
	public static byte[] objectToByteArray(final Object object)
	{
		try
		{
			final ByteArrayOutputStream out = new ByteArrayOutputStream();
			ObjectOutputStream oos = null;
			try
			{
				oos = objectStreamFactory.newObjectOutputStream(out);
				if (Application.exists())
				{
					oos.writeObject(Application.get().getApplicationKey());
				}
				else
				{
					oos.writeObject(null);
				}
				oos.writeObject(object);
			}
			finally
			{
				if (oos != null)
				{
					oos.close();
				}
				out.close();
			}
			return out.toByteArray();
		}
		catch (Exception e)
		{
			log.error("Error serializing object " + object.getClass() + " [object=" + object + "]",
				e);
		}
		return null;
	}

	/**
	 * Sets the strategy for determining the sizes of objects.
	 * 
	 * @param objectSizeOfStrategy
	 *            the strategy. Pass null to reset to the default.
	 */
	public static void setObjectSizeOfStrategy(IObjectSizeOfStrategy objectSizeOfStrategy)
	{
		if (objectSizeOfStrategy == null)
		{
			Objects.objectSizeOfStrategy = new SerializingObjectSizeOfStrategy();
		}
		else
		{
			Objects.objectSizeOfStrategy = objectSizeOfStrategy;
		}
		log.info("using " + objectSizeOfStrategy + " for calculating object sizes");
	}

	/**
	 * Configure this utility class to use the provided {@link IObjectStreamFactory} instance.
	 * 
	 * @param objectStreamFactory
	 *            The factory instance to use. If you pass in null, the
	 *            {@link DefaultObjectStreamFactory default} will be set (again). Pass null to reset
	 *            to the default.
	 */
	public static void setObjectStreamFactory(IObjectStreamFactory objectStreamFactory)
	{
		if (objectStreamFactory == null)
		{
			Objects.objectStreamFactory = new IObjectStreamFactory.DefaultObjectStreamFactory();
		}
		else
		{
			Objects.objectStreamFactory = objectStreamFactory;
		}
		log.info("using " + Objects.objectStreamFactory + " for creating object streams");
	}

	/**
	 * Computes the size of an object. Note that this is an estimation, never an absolute accurate
	 * size.
	 * 
	 * @param object
	 *            Object to compute size of
	 * @return The size of the object in bytes
	 */
	public static long sizeof(final Object object)
	{
		return objectSizeOfStrategy.sizeOf(object);
	}

	/**
	 * 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(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(Object value, boolean trim)
	{
		String result;

		if (value == null)
		{
			result = "null";
		}
		else
		{
			result = value.toString();
			if (trim)
			{
				result = result.trim();
			}
		}
		return result;
	}

	/**
	 * @param 
	 *            class type
	 * @param className
	 *            Class to resolve
	 * @return Resolved class
	 * @throws ClassNotFoundException
	 */
	@SuppressWarnings("unchecked")
	public static  Class resolveClass(final String className) throws ClassNotFoundException
	{
		if (Application.exists())
		{
			return (Class)Application.get()
				.getApplicationSettings()
				.getClassResolver()
				.resolveClass(className);
		}
		return (Class)Class.forName(className);
	}

	/**
	 * Instantiation not allowed
	 */
	private Objects()
	{
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy