Objects.java :  » J2EE » wicket-1.4 » org » apache » wicket » util » lang » Java Open Source

Java Open Source » J2EE » wicket 1.4 
wicket 1.4 » org » apache » wicket » util » lang » Objects.java
/*
 * 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<String, Component> replacedComponents;

    private ReplaceObjectInputStream(InputStream in,
      HashMap<String, Component> 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<String, Component> replacedComponents;

    private ReplaceObjectOutputStream(OutputStream out,
      HashMap<String, Component> 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<Class<?>, 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<String, Component> 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 <CODE>equals()</CODE> 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;
  }

  /**
   * Instantiation not allowed
   */
  private Objects()
  {
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.