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