| 
001 /*002  * Copyright 2008-2015 the original author or authors.
 003  *
 004  * Licensed under the Apache License, Version 2.0 (the "License");
 005  * you may not use this file except in compliance with the License.
 006  * You may obtain a copy of the License at
 007  *
 008  *     http://www.apache.org/licenses/LICENSE-2.0
 009  *
 010  * Unless required by applicable law or agreed to in writing, software
 011  * distributed under the License is distributed on an "AS IS" BASIS,
 012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 013  * See the License for the specific language governing permissions and
 014  * limitations under the License.
 015  */
 016 package griffon.util;
 017
 018 import griffon.core.editors.ExtendedPropertyEditor;
 019 import griffon.core.editors.PropertyEditorResolver;
 020 import griffon.exceptions.GriffonException;
 021 import griffon.exceptions.TypeConversionException;
 022
 023 import javax.annotation.Nonnull;
 024 import javax.annotation.Nullable;
 025 import java.beans.PropertyEditor;
 026 import java.lang.reflect.Array;
 027 import java.math.BigDecimal;
 028 import java.math.BigInteger;
 029 import java.util.ArrayList;
 030 import java.util.Collection;
 031 import java.util.HashSet;
 032 import java.util.Iterator;
 033 import java.util.List;
 034 import java.util.Map;
 035 import java.util.Set;
 036
 037 import static griffon.util.GriffonNameUtils.isBlank;
 038 import static java.util.Objects.requireNonNull;
 039
 040 /**
 041  * Utility class for dealing with type conversions.
 042  *
 043  * @author Andres Almiray
 044  * @since 2.0.0
 045  */
 046 public final class TypeUtils {
 047
 048     private static final String ERROR_VALUE_NULL = "Argument 'value' must not be null";
 049
 050     private TypeUtils() {
 051         // prevent instantiation
 052     }
 053
 054     public static boolean castToBoolean(@Nonnull Object value) {
 055         requireNonNull(value, ERROR_VALUE_NULL);
 056         if (value instanceof Boolean) {
 057             return (Boolean) value;
 058         }
 059         return "true".equalsIgnoreCase(String.valueOf(value));
 060     }
 061
 062     public static char castToChar(@Nonnull Object value) {
 063         requireNonNull(value, ERROR_VALUE_NULL);
 064         if (value instanceof Character) {
 065             return (Character) value;
 066         }
 067         return String.valueOf(value).charAt(0);
 068     }
 069
 070     public static byte castToByte(@Nonnull Object value) {
 071         requireNonNull(value, ERROR_VALUE_NULL);
 072         if (value instanceof Number) {
 073             return ((Number) value).byteValue();
 074         }
 075         return Byte.valueOf(String.valueOf(value));
 076     }
 077
 078     public static short castToShort(@Nonnull Object value) {
 079         requireNonNull(value, ERROR_VALUE_NULL);
 080         if (value instanceof Number) {
 081             return ((Number) value).shortValue();
 082         }
 083         return Short.valueOf(String.valueOf(value));
 084     }
 085
 086     public static int castToInt(@Nonnull Object value) {
 087         requireNonNull(value, ERROR_VALUE_NULL);
 088         if (value instanceof Number) {
 089             return ((Number) value).intValue();
 090         }
 091         return Integer.valueOf(String.valueOf(value));
 092     }
 093
 094     public static long castToLong(@Nonnull Object value) {
 095         requireNonNull(value, ERROR_VALUE_NULL);
 096         if (value instanceof Number) {
 097             return ((Number) value).longValue();
 098         }
 099         return Long.valueOf(String.valueOf(value));
 100     }
 101
 102     public static float castToFloat(@Nonnull Object value) {
 103         requireNonNull(value, ERROR_VALUE_NULL);
 104         if (value instanceof Number) {
 105             return ((Number) value).floatValue();
 106         }
 107         return Float.valueOf(String.valueOf(value));
 108     }
 109
 110     public static double castToDouble(@Nonnull Object value) {
 111         requireNonNull(value, ERROR_VALUE_NULL);
 112         if (value instanceof Number) {
 113             return ((Number) value).doubleValue();
 114         }
 115         return Double.valueOf(String.valueOf(value));
 116     }
 117
 118     public static BigInteger castToBigInteger(@Nonnull Object value) {
 119         requireNonNull(value, ERROR_VALUE_NULL);
 120         if (value instanceof BigInteger) {
 121             return (BigInteger) value;
 122         }
 123         return new BigInteger(String.valueOf(value));
 124     }
 125
 126     public static BigDecimal castToBigDecimal(@Nonnull Object value) {
 127         requireNonNull(value, ERROR_VALUE_NULL);
 128         if (value instanceof BigDecimal) {
 129             return (BigDecimal) value;
 130         }
 131         return new BigDecimal(String.valueOf(value));
 132     }
 133
 134     @Nullable
 135     public static Number castToNumber(@Nonnull Object value) {
 136         requireNonNull(value, ERROR_VALUE_NULL);
 137         if (value instanceof Number) {
 138             return (Number) value;
 139         }
 140         throw new IllegalArgumentException("Don't know how to cast '" + value + "' to " + Number.class.getName());
 141     }
 142
 143     public static boolean castToBoolean(@Nullable Object value, boolean defaultValue) {
 144         return value == null ? defaultValue : castToBoolean(value);
 145     }
 146
 147     public static char castToChar(@Nullable Object value, char defaultValue) {
 148         return value == null ? defaultValue : castToChar(value);
 149     }
 150
 151     public static byte castToByte(@Nullable Object value, byte defaultValue) {
 152         return value == null ? defaultValue : castToByte(value);
 153     }
 154
 155     public static short castToShort(@Nullable Object value, short defaultValue) {
 156         return value == null ? defaultValue : castToShort(value);
 157     }
 158
 159     public static int castToInt(@Nullable Object value, int defaultValue) {
 160         return value == null ? defaultValue : castToInt(value);
 161     }
 162
 163     public static long castToLong(@Nullable Object value, long defaultValue) {
 164         return value == null ? defaultValue : castToLong(value);
 165     }
 166
 167     public static float castToFloat(@Nullable Object value, float defaultValue) {
 168         return value == null ? defaultValue : castToFloat(value);
 169     }
 170
 171     public static double castToDouble(@Nullable Object value, double defaultValue) {
 172         return value == null ? defaultValue : castToDouble(value);
 173     }
 174
 175     @Nullable
 176     public static Number castToNumber(@Nullable Object value, @Nullable Number defaultValue) {
 177         return value == null ? defaultValue : castToNumber(value);
 178     }
 179
 180     @Nullable
 181     public static BigInteger castToBigInteger(@Nullable Object value, @Nullable BigInteger defaultValue) {
 182         return value == null ? defaultValue : castToBigInteger(value);
 183     }
 184
 185     @Nullable
 186     public static BigDecimal castToBigDecimal(@Nullable Object value, @Nullable BigDecimal defaultValue) {
 187         return value == null ? defaultValue : castToBigDecimal(value);
 188     }
 189
 190     @Nonnull
 191     public static <T> T convertValue(@Nonnull Class<T> targetType, @Nonnull Object value) {
 192         return convertValue(targetType, value, null);
 193     }
 194
 195     @Nonnull
 196     @SuppressWarnings("unchecked")
 197     public static <T> T convertValue(@Nonnull Class<T> targetType, @Nonnull Object value, @Nullable String format) {
 198         requireNonNull(targetType, "Argument 'targetType' must not be null");
 199         requireNonNull(value, ERROR_VALUE_NULL);
 200         if (targetType.isAssignableFrom(value.getClass())) {
 201             return (T) value;
 202         }
 203
 204         if (null == format) {
 205             if (isBoolean(targetType) && isBoolean(value.getClass())) {
 206                 return (T) value;
 207             } else if (isCharacter(targetType) && isCharacter(value.getClass())) {
 208                 return (T) value;
 209             } else if (isNumber(targetType) && isNumber(value.getClass())) {
 210                 if (isByte(targetType)) {
 211                     return (T) ((Byte) castToByte(value));
 212                 } else if (isShort(targetType)) {
 213                     return (T) ((Short) castToShort(value));
 214                 } else if (isInteger(targetType)) {
 215                     return (T) ((Integer) castToInt(value));
 216                 } else if (isLong(targetType)) {
 217                     return (T) ((Long) castToLong(value));
 218                 } else if (isFloat(targetType)) {
 219                     return (T) ((Float) castToFloat(value));
 220                 } else if (isDouble(targetType)) {
 221                     return (T) ((Double) castToDouble(value));
 222                 } else if (isBigInteger(targetType)) {
 223                     return targetType.cast(BigInteger.valueOf(((Number) value).longValue()));
 224                 } else if (isBigDecimal(targetType)) {
 225                     return targetType.cast(BigDecimal.valueOf(((Number) value).doubleValue()));
 226                 }
 227             }
 228         }
 229
 230         PropertyEditor targetEditor = resolveTargetPropertyEditor(targetType, format);
 231         if (targetEditor != null) {
 232             targetEditor.setValue(value);
 233             return (T) targetEditor.getValue();
 234         }
 235
 236         throw new TypeConversionException(value, targetType);
 237     }
 238
 239     @Nullable
 240     private static <T> PropertyEditor resolveTargetPropertyEditor(@Nonnull Class<T> targetType, @Nullable String format) {
 241         PropertyEditor editor = doResolveTargetPropertyEditor(targetType);
 242         if (editor instanceof ExtendedPropertyEditor && !isBlank(format)) {
 243             ((ExtendedPropertyEditor) editor).setFormat(format);
 244         }
 245         return editor;
 246     }
 247
 248     @Nullable
 249     private static <T> PropertyEditor doResolveTargetPropertyEditor(@Nonnull Class<T> targetType) {
 250         return PropertyEditorResolver.findEditor(targetType);
 251     }
 252
 253     public static boolean isBoolean(@Nonnull Class<?> type) {
 254         return Boolean.class == type || Boolean.TYPE == type;
 255     }
 256
 257     public static boolean isCharacter(@Nonnull Class<?> type) {
 258         return Character.class == type || Character.TYPE == type;
 259     }
 260
 261     public static boolean isByte(@Nonnull Class<?> type) {
 262         return Byte.class == type || Byte.TYPE == type;
 263     }
 264
 265     public static boolean isShort(@Nonnull Class<?> type) {
 266         return Short.class == type || Short.TYPE == type;
 267     }
 268
 269     public static boolean isInteger(@Nonnull Class<?> type) {
 270         return Integer.class == type || Integer.TYPE == type;
 271     }
 272
 273     public static boolean isLong(@Nonnull Class<?> type) {
 274         return Long.class == type || Long.TYPE == type;
 275     }
 276
 277     public static boolean isFloat(@Nonnull Class<?> type) {
 278         return Float.class == type || Float.TYPE == type;
 279     }
 280
 281     public static boolean isDouble(@Nonnull Class<?> type) {
 282         return Double.class == type || Double.TYPE == type;
 283     }
 284
 285     public static boolean isBigInteger(@Nonnull Class<?> type) {
 286         return BigInteger.class == type;
 287     }
 288
 289     public static boolean isBigDecimal(@Nonnull Class<?> type) {
 290         return BigDecimal.class == type;
 291     }
 292
 293     public static boolean isNumber(@Nonnull Class<?> type) {
 294         return Number.class == type ||
 295             isByte(type) ||
 296             isShort(type) ||
 297             isInteger(type) ||
 298             isLong(type) ||
 299             isFloat(type) ||
 300             isDouble(type) ||
 301             isBigInteger(type) ||
 302             isBigDecimal(type);
 303     }
 304
 305     // == The following methods taken from
 306     // org.codehaus.groovy.runtime.DefaultGroovyMethods
 307     // org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation
 308
 309     public static boolean equals(@Nullable Object left, @Nullable Object right) {
 310         if (left == right) return true;
 311         if (left == null || right == null) return false;
 312
 313         // handle arrays on both sides as special case for efficiency
 314         Class<?> leftClass = left.getClass();
 315         Class<?> rightClass = right.getClass();
 316         if (leftClass.isArray() && rightClass.isArray()) {
 317             return arrayEqual(left, right);
 318         }
 319         if (leftClass.isArray() && leftClass.getComponentType().isPrimitive()) {
 320             left = primitiveArrayToList(left);
 321         }
 322         if (rightClass.isArray() && rightClass.getComponentType().isPrimitive()) {
 323             right = primitiveArrayToList(right);
 324         }
 325         if (left instanceof Object[] && right instanceof List) {
 326             return equals((Object[]) left, (List) right);
 327         }
 328         if (left instanceof List && right instanceof Object[]) {
 329             return equals((List) left, (Object[]) right);
 330         }
 331         if (left instanceof List && right instanceof List) {
 332             return equals((List) left, (List) right);
 333         }
 334         if (left instanceof Map.Entry && right instanceof Map.Entry) {
 335             Object k1 = ((Map.Entry) left).getKey();
 336             Object k2 = ((Map.Entry) right).getKey();
 337             if (k1 == k2 || (k1 != null && k1.equals(k2))) {
 338                 Object v1 = ((Map.Entry) left).getValue();
 339                 Object v2 = ((Map.Entry) right).getValue();
 340                 if (v1 == v2 || (v1 != null && equals(v1, v2)))
 341                     return true;
 342             }
 343             return false;
 344         }
 345
 346         return left.equals(right);
 347     }
 348
 349     public static boolean arrayEqual(@Nullable Object left, @Nullable Object right) {
 350         if (left == null) {
 351             return right == null;
 352         }
 353         if (right == null) {
 354             return false;
 355         }
 356         if (Array.getLength(left) != Array.getLength(right)) {
 357             return false;
 358         }
 359         for (int i = 0; i < Array.getLength(left); i++) {
 360             Object l = Array.get(left, i);
 361             Object r = Array.get(right, i);
 362             if (!equals(l, r)) return false;
 363         }
 364         return true;
 365     }
 366
 367     /**
 368      * Compare the contents of this array to the contents of the given array.
 369      *
 370      * @param left  an int array
 371      * @param right the array being compared
 372      * @return true if the contents of both arrays are equal.
 373      */
 374     public static boolean equals(int[] left, int[] right) {
 375         if (left == null) {
 376             return right == null;
 377         }
 378         if (right == null) {
 379             return false;
 380         }
 381         if (left == right) {
 382             return true;
 383         }
 384         if (left.length != right.length) {
 385             return false;
 386         }
 387         for (int i = 0; i < left.length; i++) {
 388             if (left[i] != right[i]) return false;
 389         }
 390         return true;
 391     }
 392
 393     /**
 394      * Determines if the contents of this array are equal to the
 395      * contents of the given list, in the same order.  This returns
 396      * <code>false</code> if either collection is <code>null</code>.
 397      *
 398      * @param left  an array
 399      * @param right the List being compared
 400      * @return true if the contents of both collections are equal
 401      */
 402     public static boolean equals(Object[] left, List right) {
 403         return doEquals(left, right);
 404     }
 405
 406     /**
 407      * Determines if the contents of this list are equal to the
 408      * contents of the given array in the same order.  This returns
 409      * <code>false</code> if either collection is <code>null</code>.
 410      *
 411      * @param left  a List
 412      * @param right the Object[] being compared to
 413      * @return true if the contents of both collections are equal
 414      */
 415     public static boolean equals(List left, Object[] right) {
 416         return doEquals(right, left);
 417     }
 418
 419     private static boolean doEquals(Object[] left, List<?> right) {
 420         if (left == null) {
 421             return right == null;
 422         }
 423         if (right == null) {
 424             return false;
 425         }
 426         if (left.length != right.size()) {
 427             return false;
 428         }
 429         for (int i = left.length - 1; i >= 0; i--) {
 430             final Object o1 = left[i];
 431             final Object o2 = right.get(i);
 432             if (o1 == null) {
 433                 if (o2 != null) return false;
 434             } else if (!equals(o1, o2)) {
 435                 return false;
 436             }
 437         }
 438         return true;
 439     }
 440
 441     /**
 442      * Compare the contents of two Lists.  Order matters.
 443      * If numbers exist in the Lists, then they are compared as numbers,
 444      * for example 2 == 2L. If both lists are <code>null</code>, the result
 445      * is true; rightwise if either list is <code>null</code>, the result
 446      * is <code>false</code>.
 447      *
 448      * @param left  a List
 449      * @param right the List being compared to
 450      * @return boolean   <code>true</code> if the contents of both lists are identical,
 451      * <code>false</code> rightwise.
 452      */
 453     public static <T> boolean equals(List<T> left, List<T> right) {
 454         if (left == null) {
 455             return right == null;
 456         }
 457         if (right == null) {
 458             return false;
 459         }
 460         if (left == right) {
 461             return true;
 462         }
 463         if (left.size() != right.size()) {
 464             return false;
 465         }
 466         final Iterator<T> it1 = left.iterator(), it2 = right.iterator();
 467         while (it1.hasNext()) {
 468             final T o1 = it1.next();
 469             final T o2 = it2.next();
 470             if (o1 == null) {
 471                 if (o2 != null) return false;
 472             } else if (!equals(o1, o2)) {
 473                 return false;
 474             }
 475         }
 476         return true;
 477     }
 478
 479     /**
 480      * Compare the contents of two Sets for equality using Groovy's coercion rules.
 481      * <p/>
 482      * Returns <tt>true</tt> if the two sets have the same size, and every member
 483      * of the specified set is contained in this set (or equivalently, every member
 484      * of this set is contained in the specified set).
 485      * If numbers exist in the sets, then they are compared as numbers,
 486      * for example 2 == 2L.  If both sets are <code>null</code>, the result
 487      * is true; rightwise if either set is <code>null</code>, the result
 488      * is <code>false</code>.
 489      *
 490      * @param left  a Set
 491      * @param right the Set being compared to
 492      * @return <tt>true</tt> if the contents of both sets are identical
 493      * @since 1.8.0
 494      */
 495     public static <T> boolean equals(Set<T> left, Set<T> right) {
 496         if (left == null) {
 497             return right == null;
 498         }
 499         if (right == null) {
 500             return false;
 501         }
 502         if (left == right) {
 503             return true;
 504         }
 505         if (left.size() != right.size()) {
 506             return false;
 507         }
 508         final Iterator<T> it1 = left.iterator();
 509         Collection<T> rightItems = new HashSet<T>(right);
 510         while (it1.hasNext()) {
 511             final Object o1 = it1.next();
 512             final Iterator<T> it2 = rightItems.iterator();
 513             T foundItem = null;
 514             boolean found = false;
 515             while (it2.hasNext() && foundItem == null) {
 516                 final T o2 = it2.next();
 517                 if (equals(o1, o2)) {
 518                     foundItem = o2;
 519                     found = true;
 520                 }
 521             }
 522             if (!found) return false;
 523             rightItems.remove(foundItem);
 524         }
 525         return rightItems.size() == 0;
 526     }
 527
 528
 529     /**
 530      * Compares two Maps treating coerced numerical values as identical.
 531      * <p/>
 532      *
 533      * @param left  this Map
 534      * @param right the Map being compared to
 535      * @return <tt>true</tt> if the contents of both maps are identical
 536      */
 537     public static <K, V> boolean equals(Map<K, V> left, Map<K, V> right) {
 538         if (left == null) {
 539             return right == null;
 540         }
 541         if (right == null) {
 542             return false;
 543         }
 544         if (left == right) {
 545             return true;
 546         }
 547         if (left.size() != right.size()) {
 548             return false;
 549         }
 550         if (!left.keySet().equals(right.keySet())) {
 551             return false;
 552         }
 553         for (K key : left.keySet()) {
 554             if (!equals(left.get(key), right.get(key))) {
 555                 return false;
 556             }
 557         }
 558         return true;
 559     }
 560
 561     /**
 562      * Allows conversion of arrays into a mutable List
 563      *
 564      * @param array an array
 565      * @return the array as a List
 566      */
 567     public static List primitiveArrayToList(Object array) {
 568         int size = Array.getLength(array);
 569         List list = new ArrayList(size);
 570         for (int i = 0; i < size; i++) {
 571             Object item = Array.get(array, i);
 572             if (item != null && item.getClass().isArray() && item.getClass().getComponentType().isPrimitive()) {
 573                 item = primitiveArrayToList(item);
 574             }
 575             list.add(item);
 576         }
 577         return list;
 578     }
 579
 580     private static boolean isValidCharacterString(Object value) {
 581         if (value instanceof String) {
 582             String s = (String) value;
 583             if (s.length() == 1) {
 584                 return true;
 585             }
 586         }
 587         return false;
 588     }
 589
 590     @SuppressWarnings("unchecked")
 591     public static int compareTo(Object left, Object right) {
 592         if (left == right) {
 593             return 0;
 594         }
 595         if (left == null) {
 596             return -1;
 597         } else if (right == null) {
 598             return 1;
 599         }
 600         if (left instanceof Comparable) {
 601             if (left instanceof Number) {
 602                 if (isValidCharacterString(right)) {
 603                     return compareTo((Number) left, (Character) castToChar(right));
 604                 }
 605                 if (right instanceof Character || right instanceof Number) {
 606                     return compareTo((Number) left, castToNumber(right));
 607                 }
 608             } else if (left instanceof Character) {
 609                 if (isValidCharacterString(right)) {
 610                     return compareTo((Character) left, (Character) castToChar(right));
 611                 }
 612                 if (right instanceof Number) {
 613                     return compareTo((Character) left, (Number) right);
 614                 }
 615             } else if (right instanceof Number) {
 616                 if (isValidCharacterString(left)) {
 617                     return compareTo((Character) castToChar(left), (Number) right);
 618                 }
 619             } else if (left instanceof String && right instanceof Character) {
 620                 return ((String) left).compareTo(right.toString());
 621             } else if (left instanceof CharSequence && right instanceof CharSequence) {
 622                 return String.valueOf(left).compareTo(String.valueOf(right));
 623             }
 624             if (left.getClass().isAssignableFrom(right.getClass())
 625                 || (right.getClass() != Object.class && right.getClass().isAssignableFrom(left.getClass())) //GROOVY-4046
 626                 || (left instanceof CharSequence && right instanceof CharSequence)) {
 627                 Comparable comparable = (Comparable) left;
 628                 return comparable.compareTo(right);
 629             }
 630         }
 631
 632         throw new GriffonException("Cannot compare " + left.getClass().getName() + " with value '" +
 633             left + "' and " + right.getClass().getName() + " with value '" + right + "'");
 634     }
 635
 636     public static int compareTo(Character left, Number right) {
 637         return compareTo(Integer.valueOf(left), right);
 638     }
 639
 640     public static int compareTo(Number left, Character right) {
 641         return compareTo(left, Integer.valueOf(right));
 642     }
 643
 644     public static int compareTo(Character left, Character right) {
 645         return compareTo(Integer.valueOf(left), right);
 646     }
 647
 648     public static int compareTo(Number left, Number right) {
 649         if (isFloatingPoint(left) || isFloatingPoint(right)) {
 650             return Double.compare(left.doubleValue(), right.doubleValue());
 651         }
 652         if (isBigDecimal(left) || isBigDecimal(right)) {
 653             return castToBigDecimal(left).compareTo(castToBigDecimal(right));
 654         }
 655         if (isBigInteger(left) || isBigInteger(right)) {
 656             return castToBigInteger(left).compareTo(castToBigInteger(right));
 657         }
 658         if (isLong(left) || isLong(right)) {
 659             return Long.compare(left.longValue(), right.longValue());
 660         }
 661         return Integer.compare(left.intValue(), right.intValue());
 662     }
 663
 664     public static boolean isFloatingPoint(Number number) {
 665         return number instanceof Double || number instanceof Float;
 666     }
 667
 668     public static boolean isInteger(Number number) {
 669         return number instanceof Integer;
 670     }
 671
 672     public static boolean isLong(Number number) {
 673         return number instanceof Long;
 674     }
 675
 676     public static boolean isBigDecimal(Number number) {
 677         return number instanceof BigDecimal;
 678     }
 679
 680     public static boolean isBigInteger(Number number) {
 681         return number instanceof BigInteger;
 682     }
 683 }
 |