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 (Boolean) value;
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 (Character) value;
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 ((Number) value).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 ((Number) value).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 ((Number) value).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 ((Number) value).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 ((Number) value).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 ((Number) value).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 (BigInteger) value;
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 (BigDecimal) value;
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 (Number) value;
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 (T) value;
196 }
197
198 if (null == format) {
199 if (isBoolean(targetType) && isBoolean(value.getClass())) {
200 return (T) value;
201 } else if (isCharacter(targetType) && isCharacter(value.getClass())) {
202 return (T) value;
203 } else if (isNumber(targetType) && isNumber(value.getClass())) {
204 if (isByte(targetType)) {
205 return (T) ((Byte) castToByte(value));
206 } else if (isShort(targetType)) {
207 return (T) ((Short) castToShort(value));
208 } else if (isInteger(targetType)) {
209 return (T) ((Integer) castToInt(value));
210 } else if (isLong(targetType)) {
211 return (T) ((Long) castToLong(value));
212 } else if (isFloat(targetType)) {
213 return (T) ((Float) castToFloat(value));
214 } else if (isDouble(targetType)) {
215 return (T) ((Double) castToDouble(value));
216 } else if (isBigInteger(targetType)) {
217 return targetType.cast(BigInteger.valueOf(((Number) value).longValue()));
218 } else if (isBigDecimal(targetType)) {
219 return targetType.cast(BigDecimal.valueOf(((Number) value).doubleValue()));
220 }
221 }
222 }
223
224 PropertyEditor targetEditor = resolveTargetPropertyEditor(targetType, format);
225 if (targetEditor != null) {
226 targetEditor.setValue(value);
227 return (T) targetEditor.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 ((ExtendedPropertyEditor) editor).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 == right) return true;
305 if (left == null || right == null) return 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, (List) right);
321 }
322 if (left instanceof List && right instanceof Object[]) {
323 return equals((List) left, (Object[]) right);
324 }
325 if (left instanceof List && right instanceof List) {
326 return equals((List) left, (List) right);
327 }
328 if (left instanceof Map.Entry && right instanceof Map.Entry) {
329 Object k1 = ((Map.Entry) left).getKey();
330 Object k2 = ((Map.Entry) right).getKey();
331 if (k1 == k2 || (k1 != null && k1.equals(k2))) {
332 Object v1 = ((Map.Entry) left).getValue();
333 Object v2 = ((Map.Entry) right).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 != null) return 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 != null) return 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 (!found) return 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 = (String) value;
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((Number) left, (Character) castToChar(right));
598 }
599 if (right instanceof Character || right instanceof Number) {
600 return compareTo((Number) left, castToNumber(right));
601 }
602 } else if (left instanceof Character) {
603 if (isValidCharacterString(right)) {
604 return compareTo((Character) left, (Character) castToChar(right));
605 }
606 if (right instanceof Number) {
607 return compareTo((Character) left, (Number) right);
608 }
609 } else if (right instanceof Number) {
610 if (isValidCharacterString(left)) {
611 return compareTo((Character) castToChar(left), (Number) right);
612 }
613 } else if (left instanceof String && right instanceof Character) {
614 return ((String) left).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 = (Comparable) left;
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 }
|