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 }