001 /*
002 * AutoBoxing.java created on 13.09.2006
003 *
004 * To change this generated comment go to
005 * Window>Preferences>Java>Code Generation>Code and Comments
006 */
007 package org.codehaus.groovy.runtime.typehandling;
008
009 import groovy.lang.GString;
010 import groovy.lang.GroovyRuntimeException;
011
012 import java.io.File;
013 import java.io.IOException;
014 import java.lang.reflect.Array;
015 import java.lang.reflect.Modifier;
016 import java.math.BigDecimal;
017 import java.math.BigInteger;
018 import java.util.ArrayList;
019 import java.util.Arrays;
020 import java.util.Collection;
021 import java.util.Collections;
022 import java.util.Iterator;
023 import java.util.List;
024 import java.util.Map;
025 import java.util.regex.Matcher;
026
027 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
028 import org.codehaus.groovy.runtime.InvokerHelper;
029 import org.codehaus.groovy.runtime.InvokerInvocationException;
030 import org.codehaus.groovy.runtime.IteratorClosureAdapter;
031 import org.codehaus.groovy.runtime.MethodClosure;
032 import org.codehaus.groovy.runtime.RegexSupport;
033
034 public class DefaultTypeTransformation {
035
036 protected static final Object[] EMPTY_ARGUMENTS = {};
037 protected static final BigInteger ONE_NEG = new BigInteger("-1");
038
039 // --------------------------------------------------------
040 // unboxing methods
041 // --------------------------------------------------------
042
043 public static byte byteUnbox(Object value) {
044 Number n = castToNumber(value);
045 return n.byteValue();
046 }
047
048 public static char charUnbox(Object value) {
049 return castToChar(value);
050 }
051
052 public static short shortUnbox(Object value) {
053 Number n = castToNumber(value);
054 return n.shortValue();
055 }
056
057 public static int intUnbox(Object value) {
058 Number n = castToNumber(value);
059 return n.intValue();
060 }
061
062 public static boolean booleanUnbox(Object value) {
063 return castToBoolean(value);
064 }
065
066 public static long longUnbox(Object value) {
067 Number n = castToNumber(value);
068 return n.longValue();
069 }
070
071 public static float floatUnbox(Object value) {
072 Number n = castToNumber(value);
073 return n.floatValue();
074 }
075
076 public static double doubleUnbox(Object value) {
077 Number n = castToNumber(value);
078 return n.doubleValue();
079 }
080
081 // --------------------------------------------------------
082 // boxing methods
083 // --------------------------------------------------------
084
085 public static Object box(boolean value) {
086 return value ? Boolean.TRUE : Boolean.FALSE;
087 }
088
089 public static Object box(byte value) {
090 return new Byte(value);
091 }
092
093 public static Object box(char value) {
094 return new Character(value);
095 }
096
097 public static Object box(short value) {
098 return new Short(value);
099 }
100
101 public static Object box(int value) {
102 return IntegerCache.integerValue(value);
103 }
104
105 public static Object box(long value) {
106 return new Long(value);
107 }
108
109 public static Object box(float value) {
110 return new Float(value);
111 }
112
113 public static Object box(double value) {
114 return new Double(value);
115 }
116
117 public static Number castToNumber(Object object) {
118 if (object instanceof Number) return (Number) object;
119 if (object instanceof Character) {
120 return new Integer(((Character) object).charValue());
121 } else if (object instanceof String) {
122 String c = (String) object;
123 if (c.length() == 1) {
124 return new Integer(c.charAt(0));
125 }
126 else {
127 throw new GroovyCastException(c,Integer.class);
128 }
129 }
130 throw new GroovyCastException(object,Number.class);
131 }
132
133 public static boolean castToBoolean(Object object) {
134 if (object instanceof Boolean) {
135 Boolean booleanValue = (Boolean) object;
136 return booleanValue.booleanValue();
137 }
138 else if (object instanceof Matcher) {
139 Matcher matcher = (Matcher) object;
140 RegexSupport.setLastMatcher(matcher);
141 return matcher.find();
142 }
143 else if (object instanceof Collection) {
144 Collection collection = (Collection) object;
145 return !collection.isEmpty();
146 }
147 else if (object instanceof Map) {
148 Map map = (Map) object;
149 return !map.isEmpty();
150 }
151 else if (object instanceof String || object instanceof GString) {
152 String string = object.toString();
153 return string.length() > 0;
154 }
155 else if (object instanceof Character) {
156 Character c = (Character) object;
157 return c.charValue() != 0;
158 }
159 else if (object instanceof Number) {
160 Number n = (Number) object;
161 return n.doubleValue() != 0;
162 }
163 else {
164 return object != null;
165 }
166 }
167
168 public static char castToChar(Object object) {
169 if (object instanceof Character) {
170 return ((Character) object).charValue();
171 } else if (object instanceof Number) {
172 Number value = (Number) object;
173 return (char) value.intValue();
174 } else {
175 String text = object.toString();
176 if (text.length() == 1) {
177 return text.charAt(0);
178 }
179 else {
180 throw new GroovyCastException(text,char.class);
181 }
182 }
183 }
184
185 public static Object castToType(Object object, Class type) {
186 if (object == null) {
187 return null;
188 }
189
190 if (type == object.getClass()) return object;
191
192 // TODO we should move these methods to groovy method, like g$asType() so that
193 // we can use operator overloading to customize on a per-type basis
194 if (type.isArray()) {
195 return asArray(object, type);
196
197 }
198 if (type.isInstance(object)) {
199 return object;
200 }
201 if (Collection.class.isAssignableFrom(type)) {
202 if (object.getClass().isArray()) {
203 Collection answer;
204 int modifiers = type.getModifiers();
205 if (type.isAssignableFrom(ArrayList.class) && (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers))) {
206 answer = new ArrayList();
207 } else {
208 // lets call the collections constructor
209 // passing in the list wrapper
210 try {
211 answer = (Collection) type.newInstance();
212 }
213 catch (Exception e) {
214 throw new GroovyCastException("Could not instantiate instance of: " + type.getName() + ". Reason: " + e);
215 }
216 }
217
218 // we cannot just wrap in a List as we support primitive type arrays
219 int length = Array.getLength(object);
220 for (int i = 0; i < length; i++) {
221 Object element = Array.get(object, i);
222 answer.add(element);
223 }
224 return answer;
225 }
226 }
227 if (type == String.class) {
228 return object.toString();
229 } else if (type == Character.class) {
230 return box(castToChar(object));
231 } else if (type == Boolean.class) {
232 return box(castToBoolean(object));
233 } else if (Number.class.isAssignableFrom(type)) {
234 Number n = castToNumber(object);
235 if (type == Byte.class) {
236 return new Byte(n.byteValue());
237 } else if (type == Character.class) {
238 return new Character((char) n.intValue());
239 } else if (type == Short.class) {
240 return new Short(n.shortValue());
241 } else if (type == Integer.class) {
242 return new Integer(n.intValue());
243 } else if (type == Long.class) {
244 return new Long(n.longValue());
245 } else if (type == Float.class) {
246 return new Float(n.floatValue());
247 } else if (type == Double.class) {
248 Double answer = new Double(n.doubleValue());
249 //throw a runtime exception if conversion would be out-of-range for the type.
250 if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY
251 || answer.doubleValue() == Double.POSITIVE_INFINITY)) {
252 throw new GroovyRuntimeException("Automatic coercion of " + n.getClass().getName()
253 + " value " + n + " to double failed. Value is out of range.");
254 }
255 return answer;
256 } else if (type == BigDecimal.class) {
257 return new BigDecimal(n.toString());
258 } else if (type == BigInteger.class) {
259 if (object instanceof Float || object instanceof Double) {
260 BigDecimal bd = new BigDecimal(n.doubleValue());
261 return bd.toBigInteger();
262 } else if (object instanceof BigDecimal) {
263 return ((BigDecimal) object).toBigInteger();
264 } else {
265 return new BigInteger(n.toString());
266 }
267 }
268 } else if (type.isPrimitive()) {
269 if (type == boolean.class) {
270 return box(booleanUnbox(object));
271 } else if (type == byte.class) {
272 return box(byteUnbox(object));
273 } else if (type == char.class) {
274 return box(charUnbox(object));
275 } else if (type == short.class) {
276 return box(shortUnbox(object));
277 } else if (type == int.class) {
278 return box(intUnbox(object));
279 } else if (type == long.class) {
280 return box(longUnbox(object));
281 } else if (type == float.class) {
282 return box(floatUnbox(object));
283 } else if (type == double.class) {
284 Double answer = new Double(doubleUnbox(object));
285 //throw a runtime exception if conversion would be out-of-range for the type.
286 if (!(object instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY
287 || answer.doubleValue() == Double.POSITIVE_INFINITY)) {
288 throw new GroovyRuntimeException("Automatic coercion of " + object.getClass().getName()
289 + " value " + object + " to double failed. Value is out of range.");
290 }
291 return answer;
292 }
293 }
294 Object[] args = null;
295 if (object instanceof Collection) {
296 Collection list = (Collection) object;
297 args = list.toArray();
298 } else if (object instanceof Object[]) {
299 args = (Object[]) object;
300 } else if (object instanceof Map) {
301 // emulate named params constructor
302 args = new Object[1];
303 args[0] = object;
304 }
305 if (args != null) {
306 // lets try invoke the constructor with the list as arguments
307 // such as for creating a Dimension, Point, Color etc.
308 try {
309 return InvokerHelper.invokeConstructorOf(type, args);
310 } catch (InvokerInvocationException iie){
311 throw iie;
312 } catch (Exception e) {
313 // lets ignore exception and return the original object
314 // as the caller has more context to be able to throw a more
315 // meaningful exception
316 }
317 }
318 throw new GroovyCastException(object,type);
319 }
320
321 public static Object asArray(Object object, Class type) {
322 Collection list = asCollection(object);
323 int size = list.size();
324 Class elementType = type.getComponentType();
325 Object array = Array.newInstance(elementType, size);
326 int idx = 0;
327
328 if (boolean.class.equals(elementType)) {
329 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
330 Object element = iter.next();
331 Array.setBoolean(array, idx, booleanUnbox(element));
332 }
333 }
334 else if (byte.class.equals(elementType)) {
335 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
336 Object element = iter.next();
337 Array.setByte(array, idx, byteUnbox(element));
338 }
339 }
340 else if (char.class.equals(elementType)) {
341 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
342 Object element = iter.next();
343 Array.setChar(array, idx, charUnbox(element));
344 }
345 }
346 else if (double.class.equals(elementType)) {
347 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
348 Object element = iter.next();
349 Array.setDouble(array, idx, doubleUnbox(element));
350 }
351 }
352 else if (float.class.equals(elementType)) {
353 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
354 Object element = iter.next();
355 Array.setFloat(array, idx, floatUnbox(element));
356 }
357 }
358 else if (int.class.equals(elementType)) {
359 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
360 Object element = iter.next();
361 Array.setInt(array, idx, intUnbox(element));
362 }
363 }
364 else if (long.class.equals(elementType)) {
365 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
366 Object element = iter.next();
367 Array.setLong(array, idx, longUnbox(element));
368 }
369 }
370 else if (short.class.equals(elementType)) {
371 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
372 Object element = iter.next();
373 Array.setShort(array, idx, shortUnbox(element));
374 }
375 }
376 else {
377 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
378 Object element = iter.next();
379 Object coercedElement = castToType(element, elementType);
380 Array.set(array, idx, coercedElement);
381 }
382 }
383 return array;
384 }
385
386 public static Collection asCollection(Object value) {
387 if (value == null) {
388 return Collections.EMPTY_LIST;
389 }
390 else if (value instanceof Collection) {
391 return (Collection) value;
392 }
393 else if (value instanceof Map) {
394 Map map = (Map) value;
395 return map.entrySet();
396 }
397 else if (value.getClass().isArray()) {
398 if (value.getClass().getComponentType().isPrimitive()) {
399 return primitiveArrayToList(value);
400 }
401 return Arrays.asList((Object[]) value);
402 }
403 else if (value instanceof MethodClosure) {
404 MethodClosure method = (MethodClosure) value;
405 IteratorClosureAdapter adapter = new IteratorClosureAdapter(method.getDelegate());
406 method.call(adapter);
407 return adapter.asList();
408 }
409 else if (value instanceof String) {
410 return DefaultGroovyMethods.toList((String) value);
411 }
412 else if (value instanceof File) {
413 try {
414 return DefaultGroovyMethods.readLines((File) value);
415 }
416 catch (IOException e) {
417 throw new GroovyRuntimeException("Error reading file: " + value, e);
418 }
419 }
420 else {
421 // lets assume its a collection of 1
422 return Collections.singletonList(value);
423 }
424 }
425
426 /**
427 * Allows conversion of arrays into a mutable List
428 *
429 * @return the array as a List
430 */
431 public static List primitiveArrayToList(Object array) {
432 int size = Array.getLength(array);
433 List list = new ArrayList(size);
434 for (int i = 0; i < size; i++) {
435 list.add(Array.get(array, i));
436 }
437 return list;
438 }
439
440 /**
441 * Compares the two objects handling nulls gracefully and performing numeric type coercion if required
442 */
443 public static int compareTo(Object left, Object right) {
444 if (left == right) {
445 return 0;
446 }
447 if (left == null) {
448 return -1;
449 }
450 else if (right == null) {
451 return 1;
452 }
453 if (left instanceof Comparable) {
454 if (left instanceof Number) {
455 if (isValidCharacterString(right)) {
456 return castToChar(left) - castToChar(right);
457 } else if (right instanceof Character || right instanceof Number) {
458 return DefaultGroovyMethods.compareTo((Number) left, castToNumber(right));
459 }
460 }
461 else if (left instanceof Character) {
462 if (isValidCharacterString(right)) {
463 return castToChar(left) - castToChar(right);
464 }
465 else if (right instanceof Number) {
466 return castToChar(left) - castToChar(right);
467 }
468 }
469 else if (right instanceof Number) {
470 if (isValidCharacterString(left)) {
471 return castToChar(left) - castToChar(right);
472 }
473 }
474 else if (left instanceof String && right instanceof Character) {
475 return ((String) left).compareTo(right.toString());
476 }
477 else if (left instanceof String && right instanceof GString) {
478 return ((String) left).compareTo(right.toString());
479 }
480 Comparable comparable = (Comparable) left;
481 return comparable.compareTo(right);
482 }
483
484 if (left.getClass().isArray()) {
485 Collection leftList = asCollection(left);
486 if (right.getClass().isArray()) {
487 right = asCollection(right);
488 }
489 return ((Comparable) leftList).compareTo(right);
490 }
491 throw new GroovyRuntimeException("Cannot compare values: " + left + " and " + right);
492 }
493
494 public static boolean compareEqual(Object left, Object right) {
495 if (left == right) return true;
496 if (left == null || right == null) return false;
497 if (left instanceof Comparable) {
498 return compareTo(left, right) == 0;
499 } else if (left instanceof List && right instanceof List) {
500 return DefaultGroovyMethods.equals((List) left, (List) right);
501 } else {
502 return left.equals(right);
503 }
504 }
505
506 /**
507 * @return true if the given value is a valid character string (i.e. has length of 1)
508 */
509 private static boolean isValidCharacterString(Object value) {
510 if (value instanceof String) {
511 String s = (String) value;
512 if (s.length() == 1) {
513 return true;
514 }
515 }
516 return false;
517 }
518
519 /**
520 * @param a array of primitives
521 * @param type component type of the array
522 * @return
523 */
524 public static Object[] convertPrimitiveArray(Object a, Class type) {
525 // System.out.println("a.getClass() = " + a.getClass());
526 Object[] ans = null;
527 String elemType = type.getName();
528 if (elemType.equals("int")) {
529 // conservative coding
530 if (a.getClass().getName().equals("[Ljava.lang.Integer;")) {
531 ans = (Integer[]) a;
532 }
533 else {
534 int[] ia = (int[]) a;
535 ans = new Integer[ia.length];
536 for (int i = 0; i < ia.length; i++) {
537 int e = ia[i];
538 ans[i] = IntegerCache.integerValue(e);
539 }
540 }
541 }
542 else if (elemType.equals("char")) {
543 if (a.getClass().getName().equals("[Ljava.lang.Character;")) {
544 ans = (Character[]) a;
545 }
546 else {
547 char[] ia = (char[]) a;
548 ans = new Character[ia.length];
549 for (int i = 0; i < ia.length; i++) {
550 char e = ia[i];
551 ans[i] = new Character(e);
552 }
553 }
554 }
555 else if (elemType.equals("boolean")) {
556 if (a.getClass().getName().equals("[Ljava.lang.Boolean;")) {
557 ans = (Boolean[]) a;
558 }
559 else {
560 boolean[] ia = (boolean[]) a;
561 ans = new Boolean[ia.length];
562 for (int i = 0; i < ia.length; i++) {
563 boolean e = ia[i];
564 ans[i] = new Boolean(e);
565 }
566 }
567 }
568 else if (elemType.equals("byte")) {
569 if (a.getClass().getName().equals("[Ljava.lang.Byte;")) {
570 ans = (Byte[]) a;
571 }
572 else {
573 byte[] ia = (byte[]) a;
574 ans = new Byte[ia.length];
575 for (int i = 0; i < ia.length; i++) {
576 byte e = ia[i];
577 ans[i] = new Byte(e);
578 }
579 }
580 }
581 else if (elemType.equals("short")) {
582 if (a.getClass().getName().equals("[Ljava.lang.Short;")) {
583 ans = (Short[]) a;
584 }
585 else {
586 short[] ia = (short[]) a;
587 ans = new Short[ia.length];
588 for (int i = 0; i < ia.length; i++) {
589 short e = ia[i];
590 ans[i] = new Short(e);
591 }
592 }
593 }
594 else if (elemType.equals("float")) {
595 if (a.getClass().getName().equals("[Ljava.lang.Float;")) {
596 ans = (Float[]) a;
597 }
598 else {
599 float[] ia = (float[]) a;
600 ans = new Float[ia.length];
601 for (int i = 0; i < ia.length; i++) {
602 float e = ia[i];
603 ans[i] = new Float(e);
604 }
605 }
606 }
607 else if (elemType.equals("long")) {
608 if (a.getClass().getName().equals("[Ljava.lang.Long;")) {
609 ans = (Long[]) a;
610 }
611 else {
612 long[] ia = (long[]) a;
613 ans = new Long[ia.length];
614 for (int i = 0; i < ia.length; i++) {
615 long e = ia[i];
616 ans[i] = new Long(e);
617 }
618 }
619 }
620 else if (elemType.equals("double")) {
621 if (a.getClass().getName().equals("[Ljava.lang.Double;")) {
622 ans = (Double[]) a;
623 }
624 else {
625 double[] ia = (double[]) a;
626 ans = new Double[ia.length];
627 for (int i = 0; i < ia.length; i++) {
628 double e = ia[i];
629 ans[i] = new Double(e);
630 }
631 }
632 }
633 return ans;
634 }
635
636 public static int[] convertToIntArray(Object a) {
637 int[] ans = null;
638
639 // conservative coding
640 if (a.getClass().getName().equals("[I")) {
641 ans = (int[]) a;
642 }
643 else {
644 Object[] ia = (Object[]) a;
645 ans = new int[ia.length];
646 for (int i = 0; i < ia.length; i++) {
647 if (ia[i] == null) {
648 continue;
649 }
650 ans[i] = ((Number) ia[i]).intValue();
651 }
652 }
653 return ans;
654 }
655
656 public static boolean[] convertToBooleanArray(Object a) {
657 boolean[] ans = null;
658
659 // conservative coding
660 if (a.getClass().getName().equals("[Z")) {
661 ans = (boolean[]) a;
662 }
663 else {
664 Object[] ia = (Object[]) a;
665 ans = new boolean[ia.length];
666 for (int i = 0; i < ia.length; i++) {
667 if (ia[i] == null) {
668 continue;
669 }
670 ans[i] = ((Boolean) ia[i]).booleanValue();
671 }
672 }
673 return ans;
674 }
675
676 public static byte[] convertToByteArray(Object a) {
677 byte[] ans = null;
678
679 // conservative coding
680 if (a.getClass().getName().equals("[B")) {
681 ans = (byte[]) a;
682 }
683 else {
684 Object[] ia = (Object[]) a;
685 ans = new byte[ia.length];
686 for (int i = 0; i < ia.length; i++) {
687 if (ia[i] != null) {
688 ans[i] = ((Number) ia[i]).byteValue();
689 }
690 }
691 }
692 return ans;
693 }
694
695 public static short[] convertToShortArray(Object a) {
696 short[] ans = null;
697
698 // conservative coding
699 if (a.getClass().getName().equals("[S")) {
700 ans = (short[]) a;
701 }
702 else {
703 Object[] ia = (Object[]) a;
704 ans = new short[ia.length];
705 for (int i = 0; i < ia.length; i++) {
706 ans[i] = ((Number) ia[i]).shortValue();
707 }
708 }
709 return ans;
710 }
711
712 public static char[] convertToCharArray(Object a) {
713 char[] ans = null;
714
715 // conservative coding
716 if (a.getClass().getName().equals("[C")) {
717 ans = (char[]) a;
718 }
719 else {
720 Object[] ia = (Object[]) a;
721 ans = new char[ia.length];
722 for (int i = 0; i < ia.length; i++) {
723 if (ia[i] == null) {
724 continue;
725 }
726 ans[i] = ((Character) ia[i]).charValue();
727 }
728 }
729 return ans;
730 }
731
732 public static long[] convertToLongArray(Object a) {
733 long[] ans = null;
734
735 // conservative coding
736 if (a.getClass().getName().equals("[J")) {
737 ans = (long[]) a;
738 }
739 else {
740 Object[] ia = (Object[]) a;
741 ans = new long[ia.length];
742 for (int i = 0; i < ia.length; i++) {
743 if (ia[i] == null) {
744 continue;
745 }
746 ans[i] = ((Number) ia[i]).longValue();
747 }
748 }
749 return ans;
750 }
751
752 public static float[] convertToFloatArray(Object a) {
753 float[] ans = null;
754
755 // conservative coding
756 if (a.getClass().getName().equals("[F")) {
757 ans = (float[]) a;
758 }
759 else {
760 Object[] ia = (Object[]) a;
761 ans = new float[ia.length];
762 for (int i = 0; i < ia.length; i++) {
763 if (ia[i] == null) {
764 continue;
765 }
766 ans[i] = ((Number) ia[i]).floatValue();
767 }
768 }
769 return ans;
770 }
771
772 public static double[] convertToDoubleArray(Object a) {
773 double[] ans = null;
774
775 // conservative coding
776 if (a.getClass().getName().equals("[D")) {
777 ans = (double[]) a;
778 }
779 else {
780 Object[] ia = (Object[]) a;
781 ans = new double[ia.length];
782 for (int i = 0; i < ia.length; i++) {
783 if (ia[i] == null) {
784 continue;
785 }
786 ans[i] = ((Number) ia[i]).doubleValue();
787 }
788 }
789 return ans;
790 }
791
792 public static Object convertToPrimitiveArray(Object a, Class type) {
793 if (type == Byte.TYPE) {
794 return convertToByteArray(a);
795 }
796 if (type == Boolean.TYPE) {
797 return convertToBooleanArray(a);
798 }
799 if (type == Short.TYPE) {
800 return convertToShortArray(a);
801 }
802 if (type == Character.TYPE) {
803 return convertToCharArray(a);
804 }
805 if (type == Integer.TYPE) {
806 return convertToIntArray(a);
807 }
808 if (type == Long.TYPE) {
809 return convertToLongArray(a);
810 }
811 if (type == Float.TYPE) {
812 return convertToFloatArray(a);
813 }
814 if (type == Double.TYPE) {
815 return convertToDoubleArray(a);
816 }
817 else {
818 return a;
819 }
820 }
821
822 }