0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: package org.apache.wicket.util.lang;
0018:
0019: import java.io.ByteArrayInputStream;
0020: import java.io.ByteArrayOutputStream;
0021: import java.io.IOException;
0022: import java.io.InputStream;
0023: import java.io.ObjectInputStream;
0024: import java.io.ObjectOutputStream;
0025: import java.io.ObjectStreamClass;
0026: import java.io.OutputStream;
0027: import java.lang.reflect.Array;
0028: import java.math.BigDecimal;
0029: import java.math.BigInteger;
0030: import java.util.HashMap;
0031:
0032: import org.apache.wicket.Application;
0033: import org.apache.wicket.Component;
0034: import org.apache.wicket.WicketRuntimeException;
0035: import org.apache.wicket.application.IClassResolver;
0036: import org.apache.wicket.settings.IApplicationSettings;
0037: import org.apache.wicket.util.io.ByteCountingOutputStream;
0038: import org.apache.wicket.util.io.IObjectStreamFactory;
0039: import org.apache.wicket.util.io.IObjectStreamFactory.DefaultObjectStreamFactory;
0040: import org.apache.wicket.util.string.Strings;
0041: import org.slf4j.Logger;
0042: import org.slf4j.LoggerFactory;
0043:
0044: /**
0045: * Object utilities.
0046: *
0047: * @author Jonathan Locke
0048: */
0049: public final class Objects {
0050: /**
0051: * Interface that enables users to plugin the way object sizes are
0052: * calculated with Wicket.
0053: */
0054: public static interface IObjectSizeOfStrategy {
0055: /**
0056: * Computes the size of an object. This typically is an estimation, not
0057: * an absolute accurate size.
0058: *
0059: * @param object
0060: * Object to compute size of
0061: * @return The size of the object in bytes.
0062: */
0063: long sizeOf(Object object);
0064: }
0065:
0066: /**
0067: * {@link IObjectSizeOfStrategy} that works by serializing the object to an
0068: * instance of {@link ByteCountingOutputStream}, which records the number
0069: * of bytes written to it. Hence, this gives the size of the object as it
0070: * would be serialized,including all the overhead of writing class headers
0071: * etc. Not very accurate (the real memory consumption should be lower) but
0072: * the best we can do in a cheap way pre JDK 5.
0073: */
0074: public static final class SerializingObjectSizeOfStrategy implements
0075: IObjectSizeOfStrategy {
0076: /**
0077: * @see org.apache.wicket.util.lang.Objects.IObjectSizeOfStrategy#sizeOf(java.lang.Object)
0078: */
0079: public long sizeOf(Object object) {
0080: if (object == null) {
0081: return 0;
0082: }
0083: try {
0084: final ByteCountingOutputStream out = new ByteCountingOutputStream();
0085: new ObjectOutputStream(out).writeObject(object);
0086: out.close();
0087: return out.size();
0088: } catch (IOException e) {
0089: return -1;
0090: }
0091: }
0092:
0093: }
0094:
0095: private static final class ReplaceObjectInputStream extends
0096: ObjectInputStream {
0097: private final ClassLoader classloader;
0098: private final HashMap replacedComponents;
0099:
0100: private ReplaceObjectInputStream(InputStream in,
0101: HashMap replacedComponents, ClassLoader classloader)
0102: throws IOException {
0103: super (in);
0104: this .replacedComponents = replacedComponents;
0105: this .classloader = classloader;
0106: enableResolveObject(true);
0107: }
0108:
0109: // This overide is required to resolve classess inside in different
0110: // bundle, i.e.
0111: // The classess can be resolved by OSGI classresolver implementation
0112: protected Class resolveClass(ObjectStreamClass desc)
0113: throws IOException, ClassNotFoundException {
0114: String className = desc.getName();
0115:
0116: try {
0117: return Class.forName(className, true, classloader);
0118: } catch (ClassNotFoundException ex1) {
0119: // ignore this exception.
0120: log
0121: .debug("Class not found by using objects own classloader, trying the IClassResolver");
0122: }
0123:
0124: Application application = Application.get();
0125: IApplicationSettings applicationSettings = application
0126: .getApplicationSettings();
0127: IClassResolver classResolver = applicationSettings
0128: .getClassResolver();
0129:
0130: Class candidate = null;
0131: try {
0132: candidate = classResolver.resolveClass(className);
0133: if (candidate == null) {
0134: candidate = super .resolveClass(desc);
0135: }
0136: } catch (WicketRuntimeException ex) {
0137: if (ex.getCause() instanceof ClassNotFoundException) {
0138: throw (ClassNotFoundException) ex.getCause();
0139: }
0140: }
0141: return candidate;
0142: }
0143:
0144: protected Object resolveObject(Object obj) throws IOException {
0145: Object replaced = replacedComponents.get(obj);
0146: if (replaced != null) {
0147: return replaced;
0148: }
0149: return super .resolveObject(obj);
0150: }
0151: }
0152:
0153: private static final class ReplaceObjectOutputStream extends
0154: ObjectOutputStream {
0155: private final HashMap replacedComponents;
0156:
0157: private ReplaceObjectOutputStream(OutputStream out,
0158: HashMap replacedComponents) throws IOException {
0159: super (out);
0160: this .replacedComponents = replacedComponents;
0161: enableReplaceObject(true);
0162: }
0163:
0164: protected Object replaceObject(Object obj) throws IOException {
0165: if (obj instanceof Component) {
0166: String name = ((Component) obj).getPath();
0167: replacedComponents.put(name, obj);
0168: return name;
0169: }
0170: return super .replaceObject(obj);
0171: }
0172: }
0173:
0174: /** Type tag meaning java.math.BigDecimal. */
0175: private static final int BIGDEC = 9;
0176:
0177: /** Type tag meaning java.math.BigInteger. */
0178: private static final int BIGINT = 6;
0179:
0180: /** Type tag meaning boolean. */
0181: private static final int BOOL = 0;
0182:
0183: /** Type tag meaning byte. */
0184: private static final int BYTE = 1;
0185:
0186: /** Type tag meaning char. */
0187: private static final int CHAR = 2;
0188:
0189: /** Type tag meaning double. */
0190: private static final int DOUBLE = 8;
0191:
0192: /** Type tag meaning float. */
0193: private static final int FLOAT = 7;
0194:
0195: /** Type tag meaning int. */
0196: private static final int INT = 4;
0197:
0198: /** log. */
0199: private static final Logger log = LoggerFactory
0200: .getLogger(Objects.class);
0201:
0202: /** Type tag meaning long. */
0203: private static final int LONG = 5;
0204:
0205: /**
0206: * The smallest type tag that represents reals as opposed to integers. You
0207: * can see whether a type tag represents reals or integers by comparing the
0208: * tag to this constant: all tags less than this constant represent
0209: * integers, and all tags greater than or equal to this constant represent
0210: * reals. Of course, you must also check for NONNUMERIC, which means it is
0211: * not a number at all.
0212: */
0213: private static final int MIN_REAL_TYPE = FLOAT;
0214:
0215: /** Type tag meaning something other than a number. */
0216: private static final int NONNUMERIC = 10;
0217:
0218: /** Type tag meaning short. */
0219: private static final int SHORT = 3;
0220:
0221: /** defaults for primitives. */
0222: private static final HashMap primitiveDefaults = new HashMap();
0223:
0224: /**
0225: * The default object stream factory to use. Keep this as a static here
0226: * opposed to in Application, as the Application most likely isn't available
0227: * in the threads we'll be using this with.
0228: */
0229: private static IObjectStreamFactory objectStreamFactory = new IObjectStreamFactory.DefaultObjectStreamFactory();
0230:
0231: /**
0232: * Strategy for calculating sizes of objects. Note: I didn't make this an
0233: * application setting as we have enough of those already, and the typical
0234: * way this probably would be used is that install a different one according
0235: * to the JDK version used, so varying them between applications doesn't
0236: * make a lot of sense.
0237: */
0238: private static IObjectSizeOfStrategy objectSizeOfStrategy = new SerializingObjectSizeOfStrategy();
0239:
0240: static {
0241: primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);
0242: primitiveDefaults.put(Byte.TYPE, new Byte((byte) 0));
0243: primitiveDefaults.put(Short.TYPE, new Short((short) 0));
0244: primitiveDefaults.put(Character.TYPE, new Character((char) 0));
0245: primitiveDefaults.put(Integer.TYPE, new Integer(0));
0246: primitiveDefaults.put(Long.TYPE, new Long(0L));
0247: primitiveDefaults.put(Float.TYPE, new Float(0.0f));
0248: primitiveDefaults.put(Double.TYPE, new Double(0.0));
0249: primitiveDefaults.put(BigInteger.class, new BigInteger("0"));
0250: primitiveDefaults.put(BigDecimal.class, new BigDecimal(0.0));
0251: }
0252:
0253: /**
0254: * Evaluates the given object as a BigDecimal.
0255: *
0256: * @param value
0257: * an object to interpret as a BigDecimal
0258: * @return the BigDecimal value implied by the given object
0259: * @throws NumberFormatException
0260: * if the given object can't be understood as a BigDecimal
0261: */
0262: public static BigDecimal bigDecValue(Object value)
0263: throws NumberFormatException {
0264: if (value == null) {
0265: return BigDecimal.valueOf(0L);
0266: }
0267: Class c = value.getClass();
0268: if (c == BigDecimal.class) {
0269: return (BigDecimal) value;
0270: }
0271: if (c == BigInteger.class) {
0272: return new BigDecimal((BigInteger) value);
0273: }
0274: if (c.getSuperclass() == Number.class) {
0275: return new BigDecimal(((Number) value).doubleValue());
0276: }
0277: if (c == Boolean.class) {
0278: return BigDecimal
0279: .valueOf(((Boolean) value).booleanValue() ? 1 : 0);
0280: }
0281: if (c == Character.class) {
0282: return BigDecimal.valueOf(((Character) value).charValue());
0283: }
0284: return new BigDecimal(stringValue(value, true));
0285: }
0286:
0287: /**
0288: * Evaluates the given object as a BigInteger.
0289: *
0290: * @param value
0291: * an object to interpret as a BigInteger
0292: * @return the BigInteger value implied by the given object
0293: * @throws NumberFormatException
0294: * if the given object can't be understood as a BigInteger
0295: */
0296: public static BigInteger bigIntValue(Object value)
0297: throws NumberFormatException {
0298: if (value == null) {
0299: return BigInteger.valueOf(0L);
0300: }
0301: Class c = value.getClass();
0302: if (c == BigInteger.class) {
0303: return (BigInteger) value;
0304: }
0305: if (c == BigDecimal.class) {
0306: return ((BigDecimal) value).toBigInteger();
0307: }
0308: if (c.getSuperclass() == Number.class) {
0309: return BigInteger.valueOf(((Number) value).longValue());
0310: }
0311: if (c == Boolean.class) {
0312: return BigInteger
0313: .valueOf(((Boolean) value).booleanValue() ? 1 : 0);
0314: }
0315: if (c == Character.class) {
0316: return BigInteger.valueOf(((Character) value).charValue());
0317: }
0318: return new BigInteger(stringValue(value, true));
0319: }
0320:
0321: /**
0322: * Evaluates the given object as a boolean: if it is a Boolean object, it's
0323: * easy; if it's a Number or a Character, returns true for non-zero objects;
0324: * and otherwise returns true for non-null objects.
0325: *
0326: * @param value
0327: * an object to interpret as a boolean
0328: * @return the boolean value implied by the given object
0329: */
0330: public static boolean booleanValue(Object value) {
0331: if (value == null) {
0332: return false;
0333: }
0334: Class c = value.getClass();
0335: if (c == Boolean.class) {
0336: return ((Boolean) value).booleanValue();
0337: }
0338: if (c == Character.class) {
0339: return ((Character) value).charValue() != 0;
0340: }
0341: if (value instanceof Number) {
0342: return ((Number) value).doubleValue() != 0;
0343: }
0344: return true; // non-null
0345: }
0346:
0347: /**
0348: * De-serializes an object from a byte array.
0349: *
0350: * @param data
0351: * The serialized object
0352: * @return The object
0353: */
0354: public static Object byteArrayToObject(final byte[] data) {
0355: try {
0356: final ByteArrayInputStream in = new ByteArrayInputStream(
0357: data);
0358: try {
0359: return objectStreamFactory.newObjectInputStream(in)
0360: .readObject();
0361: } finally {
0362: in.close();
0363: }
0364: } catch (ClassNotFoundException e) {
0365: throw new RuntimeException(
0366: "Could not deserialize object using `"
0367: + objectStreamFactory.getClass().getName()
0368: + "` object factory", e);
0369: } catch (IOException e) {
0370: throw new RuntimeException(
0371: "Could not deserialize object using `"
0372: + objectStreamFactory.getClass().getName()
0373: + "` object factory", e);
0374: }
0375: }
0376:
0377: /**
0378: * Makes a deep clone of an object by serializing and deserializing it. The
0379: * object must be fully serializable to be cloned. This method will not
0380: * clone wicket Components, it will just reuse those instances so that the
0381: * complete component tree is not copied over only the model data.
0382: *
0383: * @param object
0384: * The object to clone
0385: * @return A deep copy of the object
0386: */
0387: public static Object cloneModel(final Object object) {
0388: if (object == null) {
0389: return null;
0390: } else {
0391: try {
0392: final ByteArrayOutputStream out = new ByteArrayOutputStream(
0393: 256);
0394: final HashMap replacedObjects = new HashMap();
0395: ObjectOutputStream oos = new ReplaceObjectOutputStream(
0396: out, replacedObjects);
0397: oos.writeObject(object);
0398: ObjectInputStream ois = new ReplaceObjectInputStream(
0399: new ByteArrayInputStream(out.toByteArray()),
0400: replacedObjects, object.getClass()
0401: .getClassLoader());
0402: return ois.readObject();
0403: } catch (ClassNotFoundException e) {
0404: throw new WicketRuntimeException(
0405: "Internal error cloning object", e);
0406: } catch (IOException e) {
0407: throw new WicketRuntimeException(
0408: "Internal error cloning object", e);
0409: }
0410: }
0411: }
0412:
0413: /**
0414: * Makes a deep clone of an object by serializing and deserializing it. The
0415: * object must be fully serializable to be cloned. No extra debug info is
0416: * gathered.
0417: *
0418: * @param object
0419: * The object to clone
0420: * @return A deep copy of the object
0421: * @see #cloneObject(Object, boolean)
0422: */
0423: public static Object cloneObject(final Object object) {
0424: if (object == null) {
0425: return null;
0426: } else {
0427: try {
0428: final ByteArrayOutputStream out = new ByteArrayOutputStream(
0429: 256);
0430: ObjectOutputStream oos = new ObjectOutputStream(out);
0431: oos.writeObject(object);
0432: ObjectInputStream ois = new ObjectInputStream(
0433: new ByteArrayInputStream(out.toByteArray())) {
0434: // This overide is required to resolve classess inside in
0435: // different
0436: // bundle, i.e.
0437: // The classess can be resolved by OSGI classresolver
0438: // implementation
0439: protected Class resolveClass(ObjectStreamClass desc)
0440: throws IOException, ClassNotFoundException {
0441: String className = desc.getName();
0442:
0443: try {
0444: return Class.forName(className, true,
0445: object.getClass().getClassLoader());
0446: } catch (ClassNotFoundException ex1) {
0447: // ignore this exception.
0448: log
0449: .debug("Class not found by using objects own classloader, trying the IClassResolver");
0450: }
0451:
0452: Application application = Application.get();
0453: IApplicationSettings applicationSettings = application
0454: .getApplicationSettings();
0455: IClassResolver classResolver = applicationSettings
0456: .getClassResolver();
0457:
0458: Class candidate = null;
0459: try {
0460: candidate = classResolver
0461: .resolveClass(className);
0462: if (candidate == null) {
0463: candidate = super .resolveClass(desc);
0464: }
0465: } catch (WicketRuntimeException ex) {
0466: if (ex.getCause() instanceof ClassNotFoundException) {
0467: throw (ClassNotFoundException) ex
0468: .getCause();
0469: }
0470: }
0471: return candidate;
0472: }
0473: };
0474: return ois.readObject();
0475: } catch (ClassNotFoundException e) {
0476: throw new WicketRuntimeException(
0477: "Internal error cloning object", e);
0478: } catch (IOException e) {
0479: throw new WicketRuntimeException(
0480: "Internal error cloning object", e);
0481: }
0482: }
0483: }
0484:
0485: /**
0486: * Compares two objects for equality, even if it has to convert one of them
0487: * to the other type. If both objects are numeric they are converted to the
0488: * widest type and compared. If one is non-numeric and one is numeric the
0489: * non-numeric is converted to double and compared to the double numeric
0490: * value. If both are non-numeric and Comparable and the types are
0491: * compatible (i.e. v1 is of the same or superclass of v2's type) they are
0492: * compared with Comparable.compareTo(). If both values are non-numeric and
0493: * not Comparable or of incompatible classes this will throw and
0494: * IllegalArgumentException.
0495: *
0496: * @param v1
0497: * First value to compare
0498: * @param v2
0499: * second value to compare
0500: *
0501: * @return integer describing the comparison between the two objects. A
0502: * negative number indicates that v1 < v2. Positive indicates that
0503: * v1 > v2. Zero indicates v1 == v2.
0504: *
0505: * @throws IllegalArgumentException
0506: * if the objects are both non-numeric yet of incompatible types
0507: * or do not implement Comparable.
0508: */
0509: public static int compareWithConversion(Object v1, Object v2) {
0510: int result;
0511:
0512: if (v1 == v2) {
0513: result = 0;
0514: } else {
0515: int t1 = getNumericType(v1), t2 = getNumericType(v2), type = getNumericType(
0516: t1, t2, true);
0517:
0518: switch (type) {
0519: case BIGINT:
0520: result = bigIntValue(v1).compareTo(bigIntValue(v2));
0521: break;
0522:
0523: case BIGDEC:
0524: result = bigDecValue(v1).compareTo(bigDecValue(v2));
0525: break;
0526:
0527: case NONNUMERIC:
0528: if ((t1 == NONNUMERIC) && (t2 == NONNUMERIC)) {
0529: if ((v1 instanceof Comparable)
0530: && v1.getClass().isAssignableFrom(
0531: v2.getClass())) {
0532: result = ((Comparable) v1).compareTo(v2);
0533: break;
0534: } else {
0535: throw new IllegalArgumentException(
0536: "invalid comparison: "
0537: + v1.getClass().getName()
0538: + " and "
0539: + v2.getClass().getName());
0540: }
0541: }
0542: // else fall through
0543: case FLOAT:
0544: case DOUBLE:
0545: double dv1 = doubleValue(v1),
0546: dv2 = doubleValue(v2);
0547:
0548: return (dv1 == dv2) ? 0 : ((dv1 < dv2) ? -1 : 1);
0549:
0550: default:
0551: long lv1 = longValue(v1),
0552: lv2 = longValue(v2);
0553:
0554: return (lv1 == lv2) ? 0 : ((lv1 < lv2) ? -1 : 1);
0555: }
0556: }
0557: return result;
0558: }
0559:
0560: /**
0561: * Returns the value converted numerically to the given class type
0562: *
0563: * This method also detects when arrays are being converted and converts the
0564: * components of one array to the type of the other.
0565: *
0566: * @param value
0567: * an object to be converted to the given type
0568: * @param toType
0569: * class type to be converted to
0570: * @return converted value of the type given, or value if the value cannot
0571: * be converted to the given type.
0572: */
0573: public static Object convertValue(Object value, Class toType) {
0574: Object result = null;
0575:
0576: if (value != null) {
0577: /* If array -> array then convert components of array individually */
0578: if (value.getClass().isArray() && toType.isArray()) {
0579: Class componentType = toType.getComponentType();
0580:
0581: result = Array.newInstance(componentType, Array
0582: .getLength(value));
0583: for (int i = 0, icount = Array.getLength(value); i < icount; i++) {
0584: Array.set(result, i, convertValue(Array.get(value,
0585: i), componentType));
0586: }
0587: } else {
0588: if ((toType == Integer.class)
0589: || (toType == Integer.TYPE)) {
0590: result = new Integer((int) longValue(value));
0591: }
0592: if ((toType == Double.class) || (toType == Double.TYPE)) {
0593: result = new Double(doubleValue(value));
0594: }
0595: if ((toType == Boolean.class)
0596: || (toType == Boolean.TYPE)) {
0597: result = booleanValue(value) ? Boolean.TRUE
0598: : Boolean.FALSE;
0599: }
0600: if ((toType == Byte.class) || (toType == Byte.TYPE)) {
0601: result = new Byte((byte) longValue(value));
0602: }
0603: if ((toType == Character.class)
0604: || (toType == Character.TYPE)) {
0605: result = new Character((char) longValue(value));
0606: }
0607: if ((toType == Short.class) || (toType == Short.TYPE)) {
0608: result = new Short((short) longValue(value));
0609: }
0610: if ((toType == Long.class) || (toType == Long.TYPE)) {
0611: result = new Long(longValue(value));
0612: }
0613: if ((toType == Float.class) || (toType == Float.TYPE)) {
0614: result = new Float(doubleValue(value));
0615: }
0616: if (toType == BigInteger.class) {
0617: result = bigIntValue(value);
0618: }
0619: if (toType == BigDecimal.class) {
0620: result = bigDecValue(value);
0621: }
0622: if (toType == String.class) {
0623: result = stringValue(value);
0624: }
0625: }
0626: } else {
0627: if (toType.isPrimitive()) {
0628: result = primitiveDefaults.get(toType);
0629: }
0630: }
0631: return result;
0632: }
0633:
0634: /**
0635: * Evaluates the given object as a double-precision floating-point number.
0636: *
0637: * @param value
0638: * an object to interpret as a double
0639: * @return the double value implied by the given object
0640: * @throws NumberFormatException
0641: * if the given object can't be understood as a double
0642: */
0643: public static double doubleValue(Object value)
0644: throws NumberFormatException {
0645: if (value == null) {
0646: return 0.0;
0647: }
0648: Class c = value.getClass();
0649: if (c.getSuperclass() == Number.class) {
0650: return ((Number) value).doubleValue();
0651: }
0652: if (c == Boolean.class) {
0653: return ((Boolean) value).booleanValue() ? 1 : 0;
0654: }
0655: if (c == Character.class) {
0656: return ((Character) value).charValue();
0657: }
0658: String s = stringValue(value, true);
0659:
0660: return (s.length() == 0) ? 0.0 : Double.parseDouble(s);
0661: }
0662:
0663: /**
0664: * Returns true if a and b are equal. Either object may be null.
0665: *
0666: * @param a
0667: * Object a
0668: * @param b
0669: * Object b
0670: * @return True if the objects are equal
0671: */
0672: public static boolean equal(final Object a, final Object b) {
0673: if (a == b) {
0674: return true;
0675: }
0676:
0677: if ((a != null) && (b != null) && a.equals(b)) {
0678: return true;
0679: }
0680:
0681: return false;
0682: }
0683:
0684: /**
0685: * Returns the constant from the NumericTypes interface that best expresses
0686: * the type of an operation, which can be either numeric or not, on the two
0687: * given types.
0688: *
0689: * @param t1
0690: * type of one argument to an operator
0691: * @param t2
0692: * type of the other argument
0693: * @param canBeNonNumeric
0694: * whether the operator can be interpreted as non-numeric
0695: * @return the appropriate constant from the NumericTypes interface
0696: */
0697: public static int getNumericType(int t1, int t2,
0698: boolean canBeNonNumeric) {
0699: if (t1 == t2) {
0700: return t1;
0701: }
0702:
0703: if (canBeNonNumeric
0704: && (t1 == NONNUMERIC || t2 == NONNUMERIC || t1 == CHAR || t2 == CHAR)) {
0705: return NONNUMERIC;
0706: }
0707:
0708: if (t1 == NONNUMERIC) {
0709: t1 = DOUBLE; // Try to interpret strings as doubles...
0710: }
0711: if (t2 == NONNUMERIC) {
0712: t2 = DOUBLE; // Try to interpret strings as doubles...
0713: }
0714:
0715: if (t1 >= MIN_REAL_TYPE) {
0716: if (t2 >= MIN_REAL_TYPE) {
0717: return Math.max(t1, t2);
0718: }
0719: if (t2 < INT) {
0720: return t1;
0721: }
0722: if (t2 == BIGINT) {
0723: return BIGDEC;
0724: }
0725: return Math.max(DOUBLE, t1);
0726: } else if (t2 >= MIN_REAL_TYPE) {
0727: if (t1 < INT) {
0728: return t2;
0729: }
0730: if (t1 == BIGINT) {
0731: return BIGDEC;
0732: }
0733: return Math.max(DOUBLE, t2);
0734: } else {
0735: return Math.max(t1, t2);
0736: }
0737: }
0738:
0739: /**
0740: * Returns a constant from the NumericTypes interface that represents the
0741: * numeric type of the given object.
0742: *
0743: * @param value
0744: * an object that needs to be interpreted as a number
0745: * @return the appropriate constant from the NumericTypes interface
0746: */
0747: public static int getNumericType(Object value) {
0748: if (value != null) {
0749: Class c = value.getClass();
0750: if (c == Integer.class) {
0751: return INT;
0752: }
0753: if (c == Double.class) {
0754: return DOUBLE;
0755: }
0756: if (c == Boolean.class) {
0757: return BOOL;
0758: }
0759: if (c == Byte.class) {
0760: return BYTE;
0761: }
0762: if (c == Character.class) {
0763: return CHAR;
0764: }
0765: if (c == Short.class) {
0766: return SHORT;
0767: }
0768: if (c == Long.class) {
0769: return LONG;
0770: }
0771: if (c == Float.class) {
0772: return FLOAT;
0773: }
0774: if (c == BigInteger.class) {
0775: return BIGINT;
0776: }
0777: if (c == BigDecimal.class) {
0778: return BIGDEC;
0779: }
0780: }
0781: return NONNUMERIC;
0782: }
0783:
0784: /**
0785: * Returns the constant from the NumericTypes interface that best expresses
0786: * the type of a numeric operation on the two given objects.
0787: *
0788: * @param v1
0789: * one argument to a numeric operator
0790: * @param v2
0791: * the other argument
0792: * @return the appropriate constant from the NumericTypes interface
0793: */
0794: public static int getNumericType(Object v1, Object v2) {
0795: return getNumericType(v1, v2, false);
0796: }
0797:
0798: /**
0799: * Returns the constant from the NumericTypes interface that best expresses
0800: * the type of an operation, which can be either numeric or not, on the two
0801: * given objects.
0802: *
0803: * @param v1
0804: * one argument to an operator
0805: * @param v2
0806: * the other argument
0807: * @param canBeNonNumeric
0808: * whether the operator can be interpreted as non-numeric
0809: * @return the appropriate constant from the NumericTypes interface
0810: */
0811: public static int getNumericType(Object v1, Object v2,
0812: boolean canBeNonNumeric) {
0813: return getNumericType(getNumericType(v1), getNumericType(v2),
0814: canBeNonNumeric);
0815: }
0816:
0817: /**
0818: * Returns true if object1 is equal to object2 in either the sense that they
0819: * are the same object or, if both are non-null if they are equal in the
0820: * <CODE>equals()</CODE> sense.
0821: *
0822: * @param object1
0823: * First object to compare
0824: * @param object2
0825: * Second object to compare
0826: *
0827: * @return true if v1 == v2
0828: */
0829: public static boolean isEqual(Object object1, Object object2) {
0830: boolean result = false;
0831:
0832: if (object1 == object2) {
0833: result = true;
0834: } else {
0835: if ((object1 != null) && object1.getClass().isArray()) {
0836: if ((object2 != null) && object2.getClass().isArray()
0837: && (object2.getClass() == object1.getClass())) {
0838: result = (Array.getLength(object1) == Array
0839: .getLength(object2));
0840: if (result) {
0841: for (int i = 0, icount = Array
0842: .getLength(object1); result
0843: && (i < icount); i++) {
0844: result = isEqual(Array.get(object1, i),
0845: Array.get(object2, i));
0846: }
0847: }
0848: }
0849: } else {
0850: // Check for converted equivalence first, then equals()
0851: // equivalence
0852: result = (object1 != null)
0853: && (object2 != null)
0854: && ((compareWithConversion(object1, object2) == 0) || object1
0855: .equals(object2));
0856: }
0857: }
0858: return result;
0859: }
0860:
0861: /**
0862: * Evaluates the given object as a long integer.
0863: *
0864: * @param value
0865: * an object to interpret as a long integer
0866: * @return the long integer value implied by the given object
0867: * @throws NumberFormatException
0868: * if the given object can't be understood as a long integer
0869: */
0870: public static long longValue(Object value)
0871: throws NumberFormatException {
0872: if (value == null) {
0873: return 0L;
0874: }
0875: Class c = value.getClass();
0876: if (c.getSuperclass() == Number.class) {
0877: return ((Number) value).longValue();
0878: }
0879: if (c == Boolean.class) {
0880: return ((Boolean) value).booleanValue() ? 1 : 0;
0881: }
0882: if (c == Character.class) {
0883: return ((Character) value).charValue();
0884: }
0885: return Long.parseLong(stringValue(value, true));
0886: }
0887:
0888: /**
0889: * Creates a new instance using the current application's class resolver.
0890: * Returns null if className is null.
0891: *
0892: * @param className
0893: * The full class name
0894: * @return The new object instance
0895: */
0896: public static Object newInstance(final String className) {
0897: if (!Strings.isEmpty(className)) {
0898: try {
0899: Class c = Classes.resolveClass(className);
0900: if (c == null) {
0901: throw new WicketRuntimeException(
0902: "Unable to create " + className);
0903: }
0904: return c.newInstance();
0905: } catch (ClassCastException e) {
0906: throw new WicketRuntimeException("Unable to create "
0907: + className, e);
0908: } catch (InstantiationException e) {
0909: throw new WicketRuntimeException("Unable to create "
0910: + className, e);
0911: } catch (IllegalAccessException e) {
0912: throw new WicketRuntimeException("Unable to create "
0913: + className, e);
0914: }
0915: }
0916: return null;
0917: }
0918:
0919: /**
0920: * Returns a new Number object of an appropriate type to hold the given
0921: * integer value. The type of the returned object is consistent with the
0922: * given type argument, which is a constant from the NumericTypes interface.
0923: *
0924: * @param type
0925: * the nominal numeric type of the result, a constant from the
0926: * NumericTypes interface
0927: * @param value
0928: * the integer value to convert to a Number object
0929: * @return a Number object with the given value, of type implied by the type
0930: * argument
0931: */
0932: public static Number newInteger(int type, long value) {
0933: switch (type) {
0934: case BOOL:
0935: case CHAR:
0936: case INT:
0937: return new Integer((int) value);
0938:
0939: case FLOAT:
0940: if (value == value) {
0941: return new Float(value);
0942: }
0943: // else fall through:
0944: case DOUBLE:
0945: if (value == value) {
0946: return new Double(value);
0947: }
0948: // else fall through:
0949: case LONG:
0950: return new Long(value);
0951:
0952: case BYTE:
0953: return new Byte((byte) value);
0954:
0955: case SHORT:
0956: return new Short((short) value);
0957:
0958: default:
0959: return BigInteger.valueOf(value);
0960: }
0961: }
0962:
0963: /**
0964: * Serializes an object into a byte array.
0965: *
0966: * @param object
0967: * The object
0968: * @return The serialized object
0969: */
0970: public static byte[] objectToByteArray(final Object object) {
0971: try {
0972: final ByteArrayOutputStream out = new ByteArrayOutputStream();
0973: try {
0974: objectStreamFactory.newObjectOutputStream(out)
0975: .writeObject(object);
0976: } finally {
0977: out.close();
0978: }
0979: return out.toByteArray();
0980: } catch (Exception e) {
0981: log.error("Error serializing object " + object.getClass()
0982: + " [object=" + object + "]", e);
0983: }
0984: return null;
0985: }
0986:
0987: /**
0988: * Sets the strategy for determining the sizes of objects.
0989: *
0990: * @param objectSizeOfStrategy
0991: * the strategy. Pass null to reset to the default.
0992: */
0993: public static void setObjectSizeOfStrategy(
0994: IObjectSizeOfStrategy objectSizeOfStrategy) {
0995: if (objectSizeOfStrategy == null) {
0996: Objects.objectSizeOfStrategy = new SerializingObjectSizeOfStrategy();
0997: } else {
0998: Objects.objectSizeOfStrategy = objectSizeOfStrategy;
0999: }
1000: log.info("using " + objectSizeOfStrategy
1001: + " for calculating object sizes");
1002: }
1003:
1004: /**
1005: * Configure this utility class to use the provided
1006: * {@link IObjectStreamFactory} instance.
1007: *
1008: * @param objectStreamFactory
1009: * The factory instance to use. If you pass in null, the
1010: * {@link DefaultObjectStreamFactory default} will be set
1011: * (again). Pass null to reset to the default.
1012: */
1013: public static void setObjectStreamFactory(
1014: IObjectStreamFactory objectStreamFactory) {
1015: if (objectStreamFactory == null) {
1016: Objects.objectStreamFactory = new IObjectStreamFactory.DefaultObjectStreamFactory();
1017: } else {
1018: Objects.objectStreamFactory = objectStreamFactory;
1019: }
1020: log.info("using " + Objects.objectStreamFactory
1021: + " for creating object streams");
1022: }
1023:
1024: /**
1025: * Computes the size of an object. Note that this is an estimation, never an
1026: * absolute accurate size.
1027: *
1028: * @param object
1029: * Object to compute size of
1030: * @return The size of the object in bytes
1031: */
1032: public static long sizeof(final Object object) {
1033: return objectSizeOfStrategy.sizeOf(object);
1034: }
1035:
1036: /**
1037: * Evaluates the given object as a String.
1038: *
1039: * @param value
1040: * an object to interpret as a String
1041: * @return the String value implied by the given object as returned by the
1042: * toString() method, or "null" if the object is null.
1043: */
1044: public static String stringValue(Object value) {
1045: return stringValue(value, false);
1046: }
1047:
1048: /**
1049: * returns hashcode of the objects by calling obj.hashcode(). safe to use
1050: * when obj is null.
1051: *
1052: * @param obj
1053: * @return hashcode of the object or 0 if obj is null
1054: */
1055: // TODO when on Java 5, we can use Object... obj
1056: public static int hashCode(final Object[] obj) {
1057: if (obj == null || obj.length == 0) {
1058: return 0;
1059: }
1060: int result = 37;
1061: for (int i = obj.length - 1; i > -1; i--) {
1062: result = 37 * result
1063: + (obj[i] != null ? obj[i].hashCode() : 0);
1064: }
1065: return result;
1066: }
1067:
1068: /**
1069: * Evaluates the given object as a String and trims it if the trim flag is
1070: * true.
1071: *
1072: * @param value
1073: * an object to interpret as a String
1074: * @param trim
1075: * whether to trim the string
1076: * @return the String value implied by the given object as returned by the
1077: * toString() method, or "null" if the object is null.
1078: */
1079: public static String stringValue(Object value, boolean trim) {
1080: String result;
1081:
1082: if (value == null) {
1083: result = "null";
1084: } else {
1085: result = value.toString();
1086: if (trim) {
1087: result = result.trim();
1088: }
1089: }
1090: return result;
1091: }
1092:
1093: /**
1094: * Instantiation not allowed
1095: */
1096: private Objects() {
1097: }
1098: }
|