001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.util.lang;
018
019import java.lang.reflect.Array;
020import java.math.BigDecimal;
021import java.math.BigInteger;
022import java.util.HashMap;
023
024
025/**
026 * Object utilities.
027 * 
028 * @author Jonathan Locke
029 */
030public final class Objects
031{
032        /** Type tag meaning java.math.BigDecimal. */
033        private static final int BIGDEC = 9;
034
035        /** Type tag meaning java.math.BigInteger. */
036        private static final int BIGINT = 6;
037
038        /** Type tag meaning boolean. */
039        private static final int BOOL = 0;
040
041        /** Type tag meaning byte. */
042        private static final int BYTE = 1;
043
044        /** Type tag meaning char. */
045        private static final int CHAR = 2;
046
047        /** Type tag meaning double. */
048        private static final int DOUBLE = 8;
049
050        /** Type tag meaning float. */
051        private static final int FLOAT = 7;
052
053        /** Type tag meaning int. */
054        private static final int INT = 4;
055
056        /** Type tag meaning long. */
057        private static final int LONG = 5;
058
059        /**
060         * The smallest type tag that represents reals as opposed to integers. You can see whether a
061         * type tag represents reals or integers by comparing the tag to this constant: all tags less
062         * than this constant represent integers, and all tags greater than or equal to this constant
063         * represent reals. Of course, you must also check for NONNUMERIC, which means it is not a
064         * number at all.
065         */
066        private static final int MIN_REAL_TYPE = FLOAT;
067
068        /** Type tag meaning something other than a number. */
069        private static final int NONNUMERIC = 10;
070
071        /** Type tag meaning short. */
072        private static final int SHORT = 3;
073
074        /** defaults for primitives. */
075        private static final HashMap<Class<?>, Object> primitiveDefaults = Generics.newHashMap();
076
077
078        static
079        {
080                primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);
081                primitiveDefaults.put(Byte.TYPE, (byte)0);
082                primitiveDefaults.put(Short.TYPE, (short)0);
083                primitiveDefaults.put(Character.TYPE, (char)0);
084                primitiveDefaults.put(Integer.TYPE, 0);
085                primitiveDefaults.put(Long.TYPE, 0L);
086                primitiveDefaults.put(Float.TYPE, 0.0f);
087                primitiveDefaults.put(Double.TYPE, 0.0);
088                primitiveDefaults.put(BigInteger.class, new BigInteger("0"));
089                primitiveDefaults.put(BigDecimal.class, new BigDecimal(0.0));
090        }
091
092        /**
093         * Evaluates the given object as a BigDecimal.
094         * 
095         * @param value
096         *            an object to interpret as a BigDecimal
097         * @return the BigDecimal value implied by the given object
098         * @throws NumberFormatException
099         *             if the given object can't be understood as a BigDecimal
100         */
101        public static BigDecimal bigDecValue(final Object value) throws NumberFormatException
102        {
103                if (value == null)
104                {
105                        return BigDecimal.valueOf(0L);
106                }
107                Class<?> c = value.getClass();
108                if (c == BigDecimal.class)
109                {
110                        return (BigDecimal)value;
111                }
112                if (c == BigInteger.class)
113                {
114                        return new BigDecimal((BigInteger)value);
115                }
116                if (c.getSuperclass() == Number.class)
117                {
118                        return new BigDecimal(((Number)value).doubleValue());
119                }
120                if (c == Boolean.class)
121                {
122                        return BigDecimal.valueOf((Boolean)value ? 1 : 0);
123                }
124                if (c == Character.class)
125                {
126                        return BigDecimal.valueOf(((Character)value).charValue());
127                }
128                return new BigDecimal(stringValue(value, true));
129        }
130
131        /**
132         * Evaluates the given object as a BigInteger.
133         * 
134         * @param value
135         *            an object to interpret as a BigInteger
136         * @return the BigInteger value implied by the given object
137         * @throws NumberFormatException
138         *             if the given object can't be understood as a BigInteger
139         */
140        public static BigInteger bigIntValue(final Object value) throws NumberFormatException
141        {
142                if (value == null)
143                {
144                        return BigInteger.valueOf(0L);
145                }
146                Class<?> c = value.getClass();
147                if (c == BigInteger.class)
148                {
149                        return (BigInteger)value;
150                }
151                if (c == BigDecimal.class)
152                {
153                        return ((BigDecimal)value).toBigInteger();
154                }
155                if (c.getSuperclass() == Number.class)
156                {
157                        return BigInteger.valueOf(((Number)value).longValue());
158                }
159                if (c == Boolean.class)
160                {
161                        return BigInteger.valueOf((Boolean)value ? 1 : 0);
162                }
163                if (c == Character.class)
164                {
165                        return BigInteger.valueOf((Character)value);
166                }
167                return new BigInteger(stringValue(value, true));
168        }
169
170        /**
171         * Evaluates the given object as a boolean: if it is a Boolean object, it's easy; if it's a
172         * Number or a Character, returns true for non-zero objects; and otherwise returns true for
173         * non-null objects.
174         * 
175         * @param value
176         *            an object to interpret as a boolean
177         * @return the boolean value implied by the given object
178         */
179        public static boolean booleanValue(final Object value)
180        {
181                if (value == null)
182                {
183                        return false;
184                }
185                Class<?> c = value.getClass();
186                if (c == Boolean.class)
187                {
188                        return (Boolean)value;
189                }
190                if (c == Character.class)
191                {
192                        return (Character)value != 0;
193                }
194                if (value instanceof Number)
195                {
196                        return ((Number)value).doubleValue() != 0;
197                }
198                return true; // non-null
199        }
200
201
202        /**
203         * Compares two objects for equality, even if it has to convert one of them to the other type.
204         * If both objects are numeric they are converted to the widest type and compared. If one is
205         * non-numeric and one is numeric the non-numeric is converted to double and compared to the
206         * double numeric value. If both are non-numeric and Comparable and the types are compatible
207         * (i.e. v1 is of the same or superclass of v2's type) they are compared with
208         * Comparable.compareTo(). If both values are non-numeric and not Comparable or of incompatible
209         * classes this will throw and IllegalArgumentException.
210         * 
211         * @param v1
212         *            First value to compare
213         * @param v2
214         *            second value to compare
215         * 
216         * @return integer describing the comparison between the two objects. A negative number
217         *         indicates that v1 &lt; v2. Positive indicates that v1 &gt; v2. Zero indicates v1 == v2.
218         * 
219         * @throws IllegalArgumentException
220         *             if the objects are both non-numeric yet of incompatible types or do not implement
221         *             Comparable.
222         */
223        @SuppressWarnings({ "unchecked", "rawtypes" })
224        public static int compareWithConversion(final Object v1, final Object v2)
225        {
226                int result;
227
228                if (v1 == v2)
229                {
230                        result = 0;
231                }
232                else
233                {
234                        int t1 = getNumericType(v1), t2 = getNumericType(v2), type = getNumericType(t1, t2,
235                                true);
236
237                        switch (type)
238                        {
239                                case BIGINT :
240                                        result = bigIntValue(v1).compareTo(bigIntValue(v2));
241                                        break;
242
243                                case BIGDEC :
244                                        result = bigDecValue(v1).compareTo(bigDecValue(v2));
245                                        break;
246
247                                case NONNUMERIC :
248                                        if ((t1 == NONNUMERIC) && (t2 == NONNUMERIC))
249                                        {
250                                                if ((v1 instanceof Comparable) &&
251                                                        v1.getClass().isAssignableFrom(v2.getClass()))
252                                                {
253                                                        result = ((Comparable)v1).compareTo(v2);
254                                                        break;
255                                                }
256                                                else
257                                                {
258                                                        throw new IllegalArgumentException("invalid comparison: " +
259                                                                v1.getClass().getName() + " and " + v2.getClass().getName());
260                                                }
261                                        }
262                                        // else fall through
263                                case FLOAT :
264                                case DOUBLE :
265                                        double dv1 = doubleValue(v1),
266                                        dv2 = doubleValue(v2);
267
268                                        return (dv1 == dv2) ? 0 : ((dv1 < dv2) ? -1 : 1);
269
270                                default :
271                                        long lv1 = longValue(v1),
272                                        lv2 = longValue(v2);
273
274                                        return (lv1 == lv2) ? 0 : ((lv1 < lv2) ? -1 : 1);
275                        }
276                }
277                return result;
278        }
279
280        /**
281         * Convert between basic Java types, i.e. primitives and their wrappers, numbers and strings.
282         * <p>
283         * This method also detects when arrays are being converted and converts the components of one
284         * array to the type of the other.
285         * 
286         * @param <T>
287         *            target type
288         * @param value
289         *            an object to be converted to the given type
290         * @param toType
291         *            class type to be converted to
292         * @return converted value of the type given, or null if the value cannot be converted to the
293         *         given type.
294         */
295        public static <T> T convertValue(final Object value, final Class<T> toType)
296        {
297                Object result = null;
298
299                if (value != null)
300                {
301                        /* If array -> array then convert components of array individually */
302                        if (value.getClass().isArray() && toType.isArray())
303                        {
304                                Class<?> componentType = toType.getComponentType();
305
306                                result = Array.newInstance(componentType, Array.getLength(value));
307                                for (int i = 0, icount = Array.getLength(value); i < icount; i++)
308                                {
309                                        Array.set(result, i, convertValue(Array.get(value, i), componentType));
310                                }
311                        }
312                        else
313                        {
314                                if ((toType == Integer.class) || (toType == Integer.TYPE))
315                                {
316                                        result = (int)longValue(value);
317                                }
318                                if ((toType == Double.class) || (toType == Double.TYPE))
319                                {
320                                        result = doubleValue(value);
321                                }
322                                if ((toType == Boolean.class) || (toType == Boolean.TYPE))
323                                {
324                                        result = booleanValue(value) ? Boolean.TRUE : Boolean.FALSE;
325                                }
326                                if ((toType == Byte.class) || (toType == Byte.TYPE))
327                                {
328                                        result = (byte)longValue(value);
329                                }
330                                if ((toType == Character.class) || (toType == Character.TYPE))
331                                {
332                                        result = (char)longValue(value);
333                                }
334                                if ((toType == Short.class) || (toType == Short.TYPE))
335                                {
336                                        result = (short)longValue(value);
337                                }
338                                if ((toType == Long.class) || (toType == Long.TYPE))
339                                {
340                                        result = longValue(value);
341                                }
342                                if ((toType == Float.class) || (toType == Float.TYPE))
343                                {
344                                        result = (float) doubleValue(value);
345                                }
346                                if (toType == BigInteger.class)
347                                {
348                                        result = bigIntValue(value);
349                                }
350                                if (toType == BigDecimal.class)
351                                {
352                                        result = bigDecValue(value);
353                                }
354                                if (toType == String.class)
355                                {
356                                        result = stringValue(value);
357                                }
358                        }
359                }
360                else
361                {
362                        if (toType.isPrimitive())
363                        {
364                                result = primitiveDefaults.get(toType);
365                        }
366                }
367                @SuppressWarnings("unchecked")
368                T finalResult = (T)result;
369                return finalResult;
370        }
371
372        /**
373         * Evaluates the given object as a double-precision floating-point number.
374         * 
375         * @param value
376         *            an object to interpret as a double
377         * @return the double value implied by the given object
378         * @throws NumberFormatException
379         *             if the given object can't be understood as a double
380         */
381        public static double doubleValue(final Object value) throws NumberFormatException
382        {
383                if (value == null)
384                {
385                        return 0.0;
386                }
387                Class<?> c = value.getClass();
388                if (c.getSuperclass() == Number.class)
389                {
390                        return ((Number)value).doubleValue();
391                }
392                if (c == Boolean.class)
393                {
394                        return (Boolean)value ? 1 : 0;
395                }
396                if (c == Character.class)
397                {
398                        return (Character)value;
399                }
400                String s = stringValue(value, true);
401
402                return (s.length() == 0) ? 0.0 : Double.parseDouble(s);
403        }
404
405        /**
406         * Returns true if a and b are equal. Either object may be null.
407         * 
408         * @param a
409         *            Object a
410         * @param b
411         *            Object b
412         * @return True if the objects are equal
413         */
414        public static boolean equal(final Object a, final Object b)
415        {
416                if (a == b)
417                {
418                        return true;
419                }
420
421                if ((a != null) && (b != null) && a.equals(b))
422                {
423                        return true;
424                }
425
426                return false;
427        }
428
429
430        /**
431         * Returns the constant from the NumericTypes interface that best expresses the type of an
432         * operation, which can be either numeric or not, on the two given types.
433         * 
434         * @param t1
435         *            type of one argument to an operator
436         * @param t2
437         *            type of the other argument
438         * @param canBeNonNumeric
439         *            whether the operator can be interpreted as non-numeric
440         * @return the appropriate constant from the NumericTypes interface
441         */
442        public static int getNumericType(int t1, int t2, final boolean canBeNonNumeric)
443        {
444                if (t1 == t2)
445                {
446                        return t1;
447                }
448
449                if (canBeNonNumeric &&
450                        ((t1 == NONNUMERIC) || (t2 == NONNUMERIC) || (t1 == CHAR) || (t2 == CHAR)))
451                {
452                        return NONNUMERIC;
453                }
454
455                if (t1 == NONNUMERIC)
456                {
457                        t1 = DOUBLE; // Try to interpret strings as doubles...
458                }
459                if (t2 == NONNUMERIC)
460                {
461                        t2 = DOUBLE; // Try to interpret strings as doubles...
462                }
463
464                if (t1 >= MIN_REAL_TYPE)
465                {
466                        if (t2 >= MIN_REAL_TYPE)
467                        {
468                                return Math.max(t1, t2);
469                        }
470                        if (t2 < INT)
471                        {
472                                return t1;
473                        }
474                        if (t2 == BIGINT)
475                        {
476                                return BIGDEC;
477                        }
478                        return Math.max(DOUBLE, t1);
479                }
480                else if (t2 >= MIN_REAL_TYPE)
481                {
482                        if (t1 < INT)
483                        {
484                                return t2;
485                        }
486                        if (t1 == BIGINT)
487                        {
488                                return BIGDEC;
489                        }
490                        return Math.max(DOUBLE, t2);
491                }
492                else
493                {
494                        return Math.max(t1, t2);
495                }
496        }
497
498        /**
499         * Returns a constant from the NumericTypes interface that represents the numeric type of the
500         * given object.
501         * 
502         * @param value
503         *            an object that needs to be interpreted as a number
504         * @return the appropriate constant from the NumericTypes interface
505         */
506        public static int getNumericType(final Object value)
507        {
508                if (value != null)
509                {
510                        Class<?> c = value.getClass();
511                        if (c == Integer.class)
512                        {
513                                return INT;
514                        }
515                        if (c == Double.class)
516                        {
517                                return DOUBLE;
518                        }
519                        if (c == Boolean.class)
520                        {
521                                return BOOL;
522                        }
523                        if (c == Byte.class)
524                        {
525                                return BYTE;
526                        }
527                        if (c == Character.class)
528                        {
529                                return CHAR;
530                        }
531                        if (c == Short.class)
532                        {
533                                return SHORT;
534                        }
535                        if (c == Long.class)
536                        {
537                                return LONG;
538                        }
539                        if (c == Float.class)
540                        {
541                                return FLOAT;
542                        }
543                        if (c == BigInteger.class)
544                        {
545                                return BIGINT;
546                        }
547                        if (c == BigDecimal.class)
548                        {
549                                return BIGDEC;
550                        }
551                }
552                return NONNUMERIC;
553        }
554
555        /**
556         * Returns the constant from the NumericTypes interface that best expresses the type of a
557         * numeric operation on the two given objects.
558         * 
559         * @param v1
560         *            one argument to a numeric operator
561         * @param v2
562         *            the other argument
563         * @return the appropriate constant from the NumericTypes interface
564         */
565        public static int getNumericType(final Object v1, final Object v2)
566        {
567                return getNumericType(v1, v2, false);
568        }
569
570        /**
571         * Returns the constant from the NumericTypes interface that best expresses the type of an
572         * operation, which can be either numeric or not, on the two given objects.
573         * 
574         * @param v1
575         *            one argument to an operator
576         * @param v2
577         *            the other argument
578         * @param canBeNonNumeric
579         *            whether the operator can be interpreted as non-numeric
580         * @return the appropriate constant from the NumericTypes interface
581         */
582        public static int getNumericType(final Object v1, final Object v2, final boolean canBeNonNumeric)
583        {
584                return getNumericType(getNumericType(v1), getNumericType(v2), canBeNonNumeric);
585        }
586
587        /**
588         * Returns true if object1 is equal to object2 in either the sense that they are the same object
589         * or, if both are non-null if they are equal in the <CODE>equals()</CODE> sense.
590         * 
591         * @param object1
592         *            First object to compare
593         * @param object2
594         *            Second object to compare
595         * 
596         * @return true if v1 == v2
597         */
598        public static boolean isEqual(final Object object1, final Object object2)
599        {
600                boolean result = false;
601
602                if (object1 == object2)
603                {
604                        result = true;
605                }
606                else
607                {
608                        if ((object1 != null) && object1.getClass().isArray())
609                        {
610                                if ((object2 != null) && object2.getClass().isArray() &&
611                                        (object2.getClass() == object1.getClass()))
612                                {
613                                        result = (Array.getLength(object1) == Array.getLength(object2));
614                                        if (result)
615                                        {
616                                                for (int i = 0, icount = Array.getLength(object1); result && (i < icount); i++)
617                                                {
618                                                        result = isEqual(Array.get(object1, i), Array.get(object2, i));
619                                                }
620                                        }
621                                }
622                        }
623                        else
624                        {
625                                // Check for converted equivalence first, then equals()
626                                // equivalence
627                                result = (object1 != null) && (object2 != null) &&
628                                        ((compareWithConversion(object1, object2) == 0) || object1.equals(object2));
629                        }
630                }
631                return result;
632        }
633
634        /**
635         * Evaluates the given object as a long integer.
636         * 
637         * @param value
638         *            an object to interpret as a long integer
639         * @return the long integer value implied by the given object
640         * @throws NumberFormatException
641         *             if the given object can't be understood as a long integer
642         */
643        public static long longValue(final Object value) throws NumberFormatException
644        {
645                if (value == null)
646                {
647                        return 0L;
648                }
649                Class<?> c = value.getClass();
650                if (c.getSuperclass() == Number.class)
651                {
652                        return ((Number)value).longValue();
653                }
654                if (c == Boolean.class)
655                {
656                        return (Boolean)value ? 1 : 0;
657                }
658                if (c == Character.class)
659                {
660                        return (Character)value;
661                }
662                return Long.parseLong(stringValue(value, true));
663        }
664
665
666        /**
667         * Returns a new Number object of an appropriate type to hold the given integer value. The type
668         * of the returned object is consistent with the given type argument, which is a constant from
669         * the NumericTypes interface.
670         * 
671         * @param type
672         *            the nominal numeric type of the result, a constant from the NumericTypes interface
673         * @param value
674         *            the integer value to convert to a Number object
675         * @return a Number object with the given value, of type implied by the type argument
676         */
677        public static Number newInteger(final int type, final long value)
678        {
679                switch (type)
680                {
681                        case BOOL :
682                        case CHAR :
683                        case INT :
684                                return (int)value;
685
686                        case FLOAT :
687                                return (float)value;
688
689                        case DOUBLE :
690                                return (double)value;
691
692                        case LONG :
693                                return value;
694
695                        case BYTE :
696                                return (byte)value;
697
698                        case SHORT :
699                                return (short)value;
700
701                        default :
702                                return BigInteger.valueOf(value);
703                }
704        }
705
706
707        /**
708         * Evaluates the given object as a String.
709         * 
710         * @param value
711         *            an object to interpret as a String
712         * @return the String value implied by the given object as returned by the toString() method, or
713         *         "null" if the object is null.
714         */
715        public static String stringValue(final Object value)
716        {
717                return stringValue(value, false);
718        }
719
720        /**
721         * returns hashcode of the objects by calling obj.hashcode(). safe to use when obj is null.
722         * 
723         * @param obj
724         * @return hashcode of the object or 0 if obj is null
725         */
726        public static int hashCode(final Object... obj)
727        {
728                if ((obj == null) || (obj.length == 0))
729                {
730                        return 0;
731                }
732                int result = 37;
733                for (int i = obj.length - 1; i > -1; i--)
734                {
735                        result = 37 * result + (obj[i] != null ? obj[i].hashCode() : 0);
736                }
737                return result;
738        }
739
740        /**
741         * Evaluates the given object as a String and trims it if the trim flag is true.
742         * 
743         * @param value
744         *            an object to interpret as a String
745         * @param trim
746         *            whether to trim the string
747         * @return the String value implied by the given object as returned by the toString() method, or
748         *         "null" if the object is null.
749         */
750        public static String stringValue(final Object value, final boolean trim)
751        {
752                String result;
753
754                if (value == null)
755                {
756                        result = "null";
757                }
758                else
759                {
760                        result = value.toString();
761                        if (trim)
762                        {
763                                result = result.trim();
764                        }
765                }
766                return result;
767        }
768
769        /**
770         * Returns the original object if this one is != null. If the original object is null
771         * the default one is returned. The default object has no restriction, it might be itself null.
772         *
773         * @param originalObj
774         *                      the original object
775         * @param defaultObj
776         *                      the default object
777         * @return the original object if not null, the default one otherwise.
778         */
779        public static <T> T defaultIfNull(T originalObj, T defaultObj)
780        {
781                return originalObj != null ? originalObj : defaultObj;
782        }
783
784        /**
785         * Instantiation not allowed
786         */
787        private Objects()
788        {
789        }
790
791}