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 < v2. Positive indicates that v1 > 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}