TypeUtils.java
001 /*
002  * Copyright 2008-2016 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 (Booleanvalue;
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 (Charactervalue;
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 ((Numbervalue).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 ((Numbervalue).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 ((Numbervalue).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 ((Numbervalue).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 ((Numbervalue).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 ((Numbervalue).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 (BigIntegervalue;
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 (BigDecimalvalue;
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 (Numbervalue;
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 (Tvalue;
202         }
203 
204         if (null == format) {
205             if (isBoolean(targetType&& isBoolean(value.getClass())) {
206                 return (Tvalue;
207             else if (isCharacter(targetType&& isCharacter(value.getClass())) {
208                 return (Tvalue;
209             else if (isNumber(targetType&& isNumber(value.getClass())) {
210                 if (isByte(targetType)) {
211                     return (T) ((BytecastToByte(value));
212                 else if (isShort(targetType)) {
213                     return (T) ((ShortcastToShort(value));
214                 else if (isInteger(targetType)) {
215                     return (T) ((IntegercastToInt(value));
216                 else if (isLong(targetType)) {
217                     return (T) ((LongcastToLong(value));
218                 else if (isFloat(targetType)) {
219                     return (T) ((FloatcastToFloat(value));
220                 else if (isDouble(targetType)) {
221                     return (T) ((DoublecastToDouble(value));
222                 else if (isBigInteger(targetType)) {
223                     return targetType.cast(BigInteger.valueOf(((Numbervalue).longValue()));
224                 else if (isBigDecimal(targetType)) {
225                     return targetType.cast(BigDecimal.valueOf(((Numbervalue).doubleValue()));
226                 }
227             }
228         }
229 
230         PropertyEditor targetEditor = resolveTargetPropertyEditor(targetType, format);
231         if (targetEditor != null) {
232             targetEditor.setValue(value);
233             return (TtargetEditor.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             ((ExtendedPropertyEditoreditor).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 == rightreturn true;
311         if (left == null || right == nullreturn 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, (Listright);
327         }
328         if (left instanceof List && right instanceof Object[]) {
329             return equals((Listleft, (Object[]) right);
330         }
331         if (left instanceof List && right instanceof List) {
332             return equals((Listleft, (Listright);
333         }
334         if (left instanceof Map.Entry && right instanceof Map.Entry) {
335             Object k1 = ((Map.Entryleft).getKey();
336             Object k2 = ((Map.Entryright).getKey();
337             if (k1 == k2 || (k1 != null && k1.equals(k2))) {
338                 Object v1 = ((Map.Entryleft).getValue();
339                 Object v2 = ((Map.Entryright).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 != nullreturn 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 != nullreturn 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 (!foundreturn 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 = (Stringvalue;
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((Numberleft, (CharactercastToChar(right));
604                 }
605                 if (right instanceof Character || right instanceof Number) {
606                     return compareTo((Numberleft, castToNumber(right));
607                 }
608             else if (left instanceof Character) {
609                 if (isValidCharacterString(right)) {
610                     return compareTo((Characterleft, (CharactercastToChar(right));
611                 }
612                 if (right instanceof Number) {
613                     return compareTo((Characterleft, (Numberright);
614                 }
615             else if (right instanceof Number) {
616                 if (isValidCharacterString(left)) {
617                     return compareTo((CharactercastToChar(left)(Numberright);
618                 }
619             else if (left instanceof String && right instanceof Character) {
620                 return ((Stringleft).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 = (Comparableleft;
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 }