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