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 }
|