001 /* 002 $Id: Invoker.java,v 1.76 2005/06/08 20:06:19 glaforge Exp $ 003 004 Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved. 005 006 Redistribution and use of this software and associated documentation 007 ("Software"), with or without modification, are permitted provided 008 that the following conditions are met: 009 010 1. Redistributions of source code must retain copyright 011 statements and notices. Redistributions must also contain a 012 copy of this document. 013 014 2. Redistributions in binary form must reproduce the 015 above copyright notice, this list of conditions and the 016 following disclaimer in the documentation and/or other 017 materials provided with the distribution. 018 019 3. The name "groovy" must not be used to endorse or promote 020 products derived from this Software without prior written 021 permission of The Codehaus. For written permission, 022 please contact info@codehaus.org. 023 024 4. Products derived from this Software may not be called "groovy" 025 nor may "groovy" appear in their names without prior written 026 permission of The Codehaus. "groovy" is a registered 027 trademark of The Codehaus. 028 029 5. Due credit should be given to The Codehaus - 030 http://groovy.codehaus.org/ 031 032 THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS 033 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT 034 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 035 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 036 THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 037 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 039 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 040 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 041 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 042 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 043 OF THE POSSIBILITY OF SUCH DAMAGE. 044 045 */ 046 package org.codehaus.groovy.runtime; 047 048 import org.codehaus.groovy.classgen.AsmClassGenerator; 049 import groovy.lang.Closure; 050 import groovy.lang.GroovyObject; 051 import groovy.lang.GroovyRuntimeException; 052 import groovy.lang.MetaClass; 053 import groovy.lang.MetaClassRegistry; 054 import groovy.lang.MissingMethodException; 055 import groovy.lang.Range; 056 import groovy.lang.SpreadList; 057 import groovy.lang.Tuple; 058 import groovy.lang.GroovyInterceptable; 059 import org.apache.xml.serialize.OutputFormat; 060 import org.apache.xml.serialize.XMLSerializer; 061 import org.w3c.dom.Element; 062 import org.w3c.dom.Node; 063 import org.w3c.dom.NodeList; 064 065 import java.io.File; 066 import java.io.IOException; 067 import java.io.StringWriter; 068 import java.lang.reflect.Array; 069 import java.lang.reflect.Method; 070 import java.math.BigDecimal; 071 import java.security.AccessController; 072 import java.security.PrivilegedAction; 073 import java.util.ArrayList; 074 import java.util.Arrays; 075 import java.util.Collection; 076 import java.util.Collections; 077 import java.util.Enumeration; 078 import java.util.Iterator; 079 import java.util.List; 080 import java.util.Map; 081 import java.util.NoSuchElementException; 082 import java.util.regex.Matcher; 083 import java.util.regex.Pattern; 084 085 /** 086 * A helper class to invoke methods or extract properties on arbitrary Java objects dynamically 087 * 088 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a> 089 * @version $Revision: 1.76 $ 090 */ 091 public class Invoker { 092 093 protected static final Object[] EMPTY_ARGUMENTS = { 094 }; 095 protected static final Class[] EMPTY_TYPES = { 096 }; 097 098 public MetaClassRegistry getMetaRegistry() { 099 return metaRegistry; 100 } 101 102 private MetaClassRegistry metaRegistry = new MetaClassRegistry(); 103 104 public MetaClass getMetaClass(Object object) { 105 return metaRegistry.getMetaClass(object.getClass()); 106 } 107 108 /** 109 * Invokes the given method on the object. 110 * 111 * @param object 112 * @param methodName 113 * @param arguments 114 * @return 115 */ 116 public Object invokeMethod(Object object, String methodName, Object arguments) { 117 /* 118 System 119 .out 120 .println( 121 "Invoker - Invoking method on object: " 122 + object 123 + " method: " 124 + methodName 125 + " arguments: " 126 + InvokerHelper.toString(arguments)); 127 */ 128 129 if (object == null) { 130 throw new NullPointerException("Cannot invoke method " + methodName + "() on null object"); 131 } 132 133 // if the object is a Class, call a static method from that class 134 if (object instanceof Class) { 135 Class theClass = (Class) object; 136 MetaClass metaClass = metaRegistry.getMetaClass(theClass); 137 return metaClass.invokeStaticMethod(object, methodName, asArray(arguments)); 138 } 139 else // it's an instance 140 { 141 // if it's not an object implementing GroovyObject (thus not builder, nor a closure) 142 if (!(object instanceof GroovyObject)) { 143 Class theClass = object.getClass(); 144 MetaClass metaClass = metaRegistry.getMetaClass(theClass); 145 return metaClass.invokeMethod(object, methodName, asArray(arguments)); 146 } 147 // it's an object implementing GroovyObject 148 else { 149 // if it's a closure, use the closure's invokeMethod() 150 if (object instanceof Closure) { 151 Closure closure = (Closure) object; 152 return closure.invokeMethod(methodName, asArray(arguments)); 153 } 154 // it's some kind of wacky object that overrides invokeMethod() to do some groovy stuff 155 // (like a proxy, a builder, some custom funny object which controls the invokation mechanism) 156 else { 157 GroovyObject groovy = (GroovyObject) object; 158 try { 159 // if it's a pure interceptable object (even intercepting toString(), clone(), ...) 160 if (groovy instanceof GroovyInterceptable) { 161 return groovy.invokeMethod(methodName, asArray(arguments)); 162 } 163 // else if there's a statically typed method or a GDK method 164 else { 165 return groovy.getMetaClass().invokeMethod(object, methodName, asArray(arguments)); 166 } 167 } 168 catch (MissingMethodException e) { 169 if (e.getMethod().equals(methodName) && object.getClass() == e.getType()) { 170 // in case there's nothing else, invoke the object's own invokeMethod() 171 return groovy.invokeMethod(methodName, asArray(arguments)); 172 } 173 else { 174 throw e; 175 } 176 } 177 } 178 } 179 } 180 } 181 182 public Object invokeSuperMethod(Object object, String methodName, Object arguments) { 183 if (object == null) { 184 throw new NullPointerException("Cannot invoke method " + methodName + "() on null object"); 185 } 186 187 Class theClass = object.getClass(); 188 189 MetaClass metaClass = metaRegistry.getMetaClass(theClass.getSuperclass()); 190 return metaClass.invokeMethod(object, methodName, asArray(arguments)); 191 } 192 193 public Object invokeStaticMethod(String type, String method, Object arguments) { 194 MetaClass metaClass = metaRegistry.getMetaClass(loadClass(type)); 195 List argumentList = asList(arguments); 196 return metaClass.invokeStaticMethod(null, method, asArray(arguments)); 197 } 198 199 public Object invokeConstructor(String type, Object arguments) { 200 return invokeConstructorOf(loadClass(type), arguments); 201 } 202 203 public Object invokeConstructorOf(Class type, Object arguments) { 204 MetaClass metaClass = metaRegistry.getMetaClass(type); 205 return metaClass.invokeConstructor(asArray(arguments)); 206 } 207 208 /** 209 * Converts the given object into an array; if its an array then just 210 * cast otherwise wrap it in an array 211 */ 212 public Object[] asArray(Object arguments) { 213 if (arguments == null) { 214 return EMPTY_ARGUMENTS; 215 } 216 else if ((arguments instanceof Object[]) && ((Object[]) arguments).length == 0) { 217 return (Object[]) arguments; 218 } 219 else if (arguments instanceof Tuple) { 220 Tuple tuple = (Tuple) arguments; 221 Object[] objects = tuple.toArray(); 222 ArrayList array = new ArrayList(); 223 for (int i = 0; i < objects.length; i++) { 224 if (objects[i] instanceof SpreadList) { 225 SpreadList slist = (SpreadList) objects[i]; 226 for (int j = 0; j < slist.size(); j++) { 227 array.add(slist.get(j)); 228 } 229 } 230 else { 231 array.add(objects[i]); 232 } 233 } 234 return array.toArray(); 235 } 236 else if (arguments instanceof Object[]) { 237 Object[] objects = (Object[]) arguments; 238 ArrayList array = new ArrayList(); 239 for (int i = 0; i < objects.length; i++) { 240 if (objects[i] instanceof SpreadList) { 241 SpreadList slist = (SpreadList) objects[i]; 242 for (int j = 0; j < slist.size(); j++) { 243 array.add(slist.get(j)); 244 } 245 } 246 else { 247 array.add(objects[i]); 248 } 249 } 250 return array.toArray(); 251 } 252 else if (arguments instanceof SpreadList) { 253 ArrayList array = new ArrayList(); 254 SpreadList slist = (SpreadList) arguments; 255 for (int j = 0; j < slist.size(); j++) { 256 array.add(slist.get(j)); 257 } 258 return array.toArray(); 259 } 260 else { 261 return new Object[]{arguments}; 262 } 263 } 264 265 public List asList(Object value) { 266 if (value == null) { 267 return Collections.EMPTY_LIST; 268 } 269 else if (value instanceof List) { 270 return (List) value; 271 } 272 else if (value.getClass().isArray()) { 273 return Arrays.asList((Object[]) value); 274 } 275 else if (value instanceof Enumeration) { 276 List answer = new ArrayList(); 277 for (Enumeration e = (Enumeration) value; e.hasMoreElements();) { 278 answer.add(e.nextElement()); 279 } 280 return answer; 281 } 282 else { 283 // lets assume its a collection of 1 284 return Collections.singletonList(value); 285 } 286 } 287 288 /** 289 * Converts the value parameter into a <code>Collection</code>. 290 * 291 * @param value value to convert 292 * @return a Collection 293 */ 294 public Collection asCollection(Object value) { 295 if (value == null) { 296 return Collections.EMPTY_LIST; 297 } 298 else if (value instanceof Collection) { 299 return (Collection) value; 300 } 301 else if (value instanceof Map) { 302 Map map = (Map) value; 303 return map.entrySet(); 304 } 305 else if (value.getClass().isArray()) { 306 if (value.getClass().getComponentType().isPrimitive()) { 307 return InvokerHelper.primitiveArrayToList(value); 308 } 309 return Arrays.asList((Object[]) value); 310 } 311 else if (value instanceof MethodClosure) { 312 MethodClosure method = (MethodClosure) value; 313 IteratorClosureAdapter adapter = new IteratorClosureAdapter(method.getDelegate()); 314 method.call(adapter); 315 return adapter.asList(); 316 } 317 else if (value instanceof String) { 318 return DefaultGroovyMethods.toList((String) value); 319 } 320 else if (value instanceof File) { 321 try { 322 return DefaultGroovyMethods.readLines((File) value); 323 } 324 catch (IOException e) { 325 throw new GroovyRuntimeException("Error reading file: " + value, e); 326 } 327 } 328 else { 329 // lets assume its a collection of 1 330 return Collections.singletonList(value); 331 } 332 } 333 334 public Iterator asIterator(Object value) { 335 if (value == null) { 336 return Collections.EMPTY_LIST.iterator(); 337 } 338 if (value instanceof Iterator) { 339 return (Iterator) value; 340 } 341 if (value instanceof NodeList) { 342 final NodeList nodeList = (NodeList) value; 343 return new Iterator() { 344 private int current = 0; 345 346 public boolean hasNext() { 347 return current < nodeList.getLength(); 348 } 349 350 public Object next() { 351 Node node = nodeList.item(current++); 352 return node; 353 } 354 355 public void remove() { 356 throw new UnsupportedOperationException("Cannot remove() from an Enumeration"); 357 } 358 }; 359 } 360 else if (value instanceof Enumeration) { 361 final Enumeration enumeration = (Enumeration) value; 362 return new Iterator() { 363 private Object last; 364 365 public boolean hasNext() { 366 return enumeration.hasMoreElements(); 367 } 368 369 public Object next() { 370 last = enumeration.nextElement(); 371 return last; 372 } 373 374 public void remove() { 375 throw new UnsupportedOperationException("Cannot remove() from an Enumeration"); 376 } 377 }; 378 } 379 else if (value instanceof Matcher) { 380 final Matcher matcher = (Matcher) value; 381 return new Iterator() { 382 private boolean found = false; 383 private boolean done = false; 384 385 public boolean hasNext() { 386 if (done) { 387 return false; 388 } 389 if (!found) { 390 found = matcher.find(); 391 if (!found) { 392 done = true; 393 } 394 } 395 return found; 396 } 397 398 public Object next() { 399 if (!found) { 400 if (!hasNext()) { 401 throw new NoSuchElementException(); 402 } 403 } 404 found = false; 405 return matcher.group(); 406 } 407 408 public void remove() { 409 throw new UnsupportedOperationException(); 410 } 411 }; 412 } 413 else { 414 try { 415 // lets try see if there's an iterator() method 416 final Method method = value.getClass().getMethod("iterator", EMPTY_TYPES); 417 418 if (method != null) { 419 AccessController.doPrivileged(new PrivilegedAction() { 420 public Object run() { 421 method.setAccessible(true); 422 return null; 423 } 424 }); 425 426 return (Iterator) method.invoke(value, EMPTY_ARGUMENTS); 427 } 428 } 429 catch (Exception e) { 430 // ignore 431 } 432 } 433 return asCollection(value).iterator(); 434 } 435 436 /** 437 * @return true if the two objects are null or the objects are equal 438 */ 439 public boolean objectsEqual(Object left, Object right) { 440 if (left == right) { 441 return true; 442 } 443 if (left != null) { 444 if (right == null) { 445 return false; 446 } 447 if (left instanceof Comparable) { 448 return compareTo(left, right) == 0; 449 } 450 else { 451 return left.equals(right); 452 } 453 } 454 return false; 455 } 456 457 public String inspect(Object self) { 458 return format(self, true); 459 } 460 461 /** 462 * Compares the two objects handling nulls gracefully and performing numeric type coercion if required 463 */ 464 public int compareTo(Object left, Object right) { 465 //System.out.println("Comparing: " + left + " to: " + right); 466 if (left == right) { 467 return 0; 468 } 469 if (left == null) { 470 return -1; 471 } 472 else if (right == null) { 473 return 1; 474 } 475 if (left instanceof Comparable) { 476 if (left instanceof Number) { 477 if (isValidCharacterString(right)) { 478 return asCharacter((Number) left).compareTo(asCharacter((String) right)); 479 } 480 return DefaultGroovyMethods.compareTo((Number) left, asNumber(right)); 481 } 482 else if (left instanceof Character) { 483 if (isValidCharacterString(right)) { 484 return ((Character) left).compareTo(asCharacter((String) right)); 485 } 486 else if (right instanceof Number) { 487 return ((Character) left).compareTo(asCharacter((Number) right)); 488 } 489 } 490 else if (right instanceof Number) { 491 if (isValidCharacterString(left)) { 492 return asCharacter((String) left).compareTo(asCharacter((Number) right)); 493 } 494 return DefaultGroovyMethods.compareTo(asNumber(left), (Number) right); 495 } 496 else if (left instanceof String && right instanceof Character) { 497 return ((String) left).compareTo(right.toString()); 498 } 499 Comparable comparable = (Comparable) left; 500 return comparable.compareTo(right); 501 } 502 if (left.getClass().isArray()) { 503 Collection leftList = asCollection(left); 504 if (right.getClass().isArray()) { 505 right = asCollection(right); 506 } 507 return ((Comparable) leftList).compareTo(right); 508 } 509 /** todo we might wanna do some type conversion here */ 510 throw new GroovyRuntimeException("Cannot compare values: " + left + " and " + right); 511 } 512 513 /** 514 * A helper method to provide some better toString() behaviour such as turning arrays 515 * into tuples 516 */ 517 public String toString(Object arguments) { 518 return format(arguments, false); 519 } 520 521 /** 522 * A helper method to format the arguments types as a comma-separated list 523 */ 524 public String toTypeString(Object[] arguments) { 525 if (arguments == null) { 526 return "null"; 527 } 528 StringBuffer argBuf = new StringBuffer(); 529 for (int i = 0; i < arguments.length; i++) { 530 if (i > 0) { 531 argBuf.append(", "); 532 } 533 argBuf.append(arguments[i] != null ? arguments[i].getClass().getName() : "null"); 534 } 535 return argBuf.toString(); 536 } 537 538 protected String format(Object arguments, boolean verbose) { 539 if (arguments == null) { 540 return "null"; 541 } 542 else if (arguments.getClass().isArray()) { 543 return format(asCollection(arguments), verbose); 544 } 545 else if (arguments instanceof Range) { 546 Range range = (Range) arguments; 547 if (verbose) { 548 return range.inspect(); 549 } 550 else { 551 return range.toString(); 552 } 553 } 554 else if (arguments instanceof List) { 555 List list = (List) arguments; 556 StringBuffer buffer = new StringBuffer("["); 557 boolean first = true; 558 for (Iterator iter = list.iterator(); iter.hasNext();) { 559 if (first) { 560 first = false; 561 } 562 else { 563 buffer.append(", "); 564 } 565 buffer.append(format(iter.next(), verbose)); 566 } 567 buffer.append("]"); 568 return buffer.toString(); 569 } 570 else if (arguments instanceof Map) { 571 Map map = (Map) arguments; 572 if (map.isEmpty()) { 573 return "[:]"; 574 } 575 StringBuffer buffer = new StringBuffer("["); 576 boolean first = true; 577 for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) { 578 if (first) { 579 first = false; 580 } 581 else { 582 buffer.append(", "); 583 } 584 Map.Entry entry = (Map.Entry) iter.next(); 585 buffer.append(format(entry.getKey(), verbose)); 586 buffer.append(":"); 587 buffer.append(format(entry.getValue(), verbose)); 588 } 589 buffer.append("]"); 590 return buffer.toString(); 591 } 592 else if (arguments instanceof Element) { 593 Element node = (Element) arguments; 594 OutputFormat format = new OutputFormat(node.getOwnerDocument()); 595 format.setOmitXMLDeclaration(true); 596 format.setIndenting(true); 597 format.setLineWidth(0); 598 format.setPreserveSpace(true); 599 StringWriter sw = new StringWriter(); 600 XMLSerializer serializer = new XMLSerializer(sw, format); 601 try { 602 serializer.asDOMSerializer(); 603 serializer.serialize(node); 604 } 605 catch (IOException e) { 606 } 607 return sw.toString(); 608 } 609 else if (arguments instanceof String) { 610 if (verbose) { 611 return "\"" + arguments + "\""; 612 } 613 else { 614 return (String) arguments; 615 } 616 } 617 else { 618 return arguments.toString(); 619 } 620 } 621 622 /** 623 * Looks up the given property of the given object 624 */ 625 public Object getProperty(Object object, String property) { 626 if (object == null) { 627 throw new NullPointerException("Cannot get property: " + property + " on null object"); 628 } 629 else if (object instanceof GroovyObject) { 630 GroovyObject pogo = (GroovyObject) object; 631 return pogo.getProperty(property); 632 } 633 else if (object instanceof Map) { 634 Map map = (Map) object; 635 return map.get(property); 636 } 637 else { 638 return metaRegistry.getMetaClass(object.getClass()).getProperty(object, property); 639 } 640 } 641 642 /** 643 * Sets the property on the given object 644 */ 645 public void setProperty(Object object, String property, Object newValue) { 646 if (object == null) { 647 throw new GroovyRuntimeException("Cannot set property on null object"); 648 } 649 else if (object instanceof GroovyObject) { 650 GroovyObject pogo = (GroovyObject) object; 651 pogo.setProperty(property, newValue); 652 } 653 else if (object instanceof Map) { 654 Map map = (Map) object; 655 map.put(property, newValue); 656 } 657 else { 658 metaRegistry.getMetaClass(object.getClass()).setProperty(object, property, newValue); 659 } 660 } 661 662 /** 663 * Looks up the given attribute (field) on the given object 664 */ 665 public Object getAttribute(Object object, String attribute) { 666 if (object == null) { 667 throw new NullPointerException("Cannot get attribute: " + attribute + " on null object"); 668 669 /** 670 } else if (object instanceof GroovyObject) { 671 GroovyObject pogo = (GroovyObject) object; 672 return pogo.getAttribute(attribute); 673 } else if (object instanceof Map) { 674 Map map = (Map) object; 675 return map.get(attribute); 676 */ 677 } 678 else { 679 return metaRegistry.getMetaClass(object.getClass()).getAttribute(object, attribute); 680 } 681 } 682 683 /** 684 * Sets the given attribute (field) on the given object 685 */ 686 public void setAttribute(Object object, String attribute, Object newValue) { 687 if (object == null) { 688 throw new GroovyRuntimeException("Cannot set attribute on null object"); 689 /* 690 } else if (object instanceof GroovyObject) { 691 GroovyObject pogo = (GroovyObject) object; 692 pogo.setProperty(attribute, newValue); 693 } else if (object instanceof Map) { 694 Map map = (Map) object; 695 map.put(attribute, newValue); 696 */ 697 } 698 else { 699 metaRegistry.getMetaClass(object.getClass()).setAttribute(object, attribute, newValue); 700 } 701 } 702 703 /** 704 * Returns the method pointer for the given object name 705 */ 706 public Closure getMethodPointer(Object object, String methodName) { 707 if (object == null) { 708 throw new NullPointerException("Cannot access method pointer for '" + methodName + "' on null object"); 709 } 710 return metaRegistry.getMetaClass(object.getClass()).getMethodPointer(object, methodName); 711 } 712 713 714 /** 715 * Attempts to load the given class via name using the current class loader 716 * for this code or the thread context class loader 717 */ 718 protected Class loadClass(String type) { 719 try { 720 return getClass().getClassLoader().loadClass(type); 721 } 722 catch (ClassNotFoundException e) { 723 try { 724 return Thread.currentThread().getContextClassLoader().loadClass(type); 725 } 726 catch (ClassNotFoundException e2) { 727 try { 728 return Class.forName(type); 729 } 730 catch (ClassNotFoundException e3) { 731 } 732 } 733 throw new GroovyRuntimeException("Could not load type: " + type, e); 734 } 735 } 736 737 /** 738 * Find the right hand regex within the left hand string and return a matcher. 739 * 740 * @param left string to compare 741 * @param right regular expression to compare the string to 742 * @return 743 */ 744 public Matcher objectFindRegex(Object left, Object right) { 745 String stringToCompare; 746 if (left instanceof String) { 747 stringToCompare = (String) left; 748 } 749 else { 750 stringToCompare = toString(left); 751 } 752 String regexToCompareTo; 753 if (right instanceof String) { 754 regexToCompareTo = (String) right; 755 } 756 else if (right instanceof Pattern) { 757 Pattern pattern = (Pattern) right; 758 return pattern.matcher(stringToCompare); 759 } 760 else { 761 regexToCompareTo = toString(right); 762 } 763 Matcher matcher = Pattern.compile(regexToCompareTo).matcher(stringToCompare); 764 return matcher; 765 } 766 767 /** 768 * Find the right hand regex within the left hand string and return a matcher. 769 * 770 * @param left string to compare 771 * @param right regular expression to compare the string to 772 * @return 773 */ 774 public boolean objectMatchRegex(Object left, Object right) { 775 Pattern pattern; 776 if (right instanceof Pattern) { 777 pattern = (Pattern) right; 778 } 779 else { 780 pattern = Pattern.compile(toString(right)); 781 } 782 String stringToCompare = toString(left); 783 Matcher matcher = pattern.matcher(stringToCompare); 784 RegexSupport.setLastMatcher(matcher); 785 return matcher.matches(); 786 } 787 788 /** 789 * Compile a regular expression from a string. 790 * 791 * @param regex 792 * @return 793 */ 794 public Pattern regexPattern(Object regex) { 795 return Pattern.compile(regex.toString()); 796 } 797 798 public Object asType(Object object, Class type) { 799 if (object == null) { 800 return null; 801 } 802 // TODO we should move these methods to groovy method, like g$asType() so that 803 // we can use operator overloading to customize on a per-type basis 804 if (type.isArray()) { 805 return asArray(object, type); 806 807 } 808 if (type.isInstance(object)) { 809 return object; 810 } 811 if (type.isAssignableFrom(Collection.class)) { 812 if (object.getClass().isArray()) { 813 // lets call the collections constructor 814 // passing in the list wrapper 815 Collection answer = null; 816 try { 817 answer = (Collection) type.newInstance(); 818 } 819 catch (Exception e) { 820 throw new ClassCastException("Could not instantiate instance of: " + type.getName() + ". Reason: " + e); 821 } 822 823 // we cannot just wrap in a List as we support primitive type arrays 824 int length = Array.getLength(object); 825 for (int i = 0; i < length; i++) { 826 Object element = Array.get(object, i); 827 answer.add(element); 828 } 829 return answer; 830 } 831 } 832 if (type.equals(String.class)) { 833 return object.toString(); 834 } 835 if (type.equals(Character.class)) { 836 if (object instanceof Number) { 837 return asCharacter((Number) object); 838 } 839 else { 840 String text = object.toString(); 841 if (text.length() == 1) { 842 return new Character(text.charAt(0)); 843 } 844 else { 845 throw new ClassCastException("Cannot cast: " + text + " to a Character"); 846 } 847 } 848 } 849 if (Number.class.isAssignableFrom(type)) { 850 if (object instanceof Character) { 851 return new Integer(((Character) object).charValue()); 852 } 853 else if (object instanceof String) { 854 String c = (String) object; 855 if (c.length() == 1) { 856 return new Integer(c.charAt(0)); 857 } 858 else { 859 throw new ClassCastException("Cannot cast: '" + c + "' to an Integer"); 860 } 861 } 862 } 863 if (object instanceof Number) { 864 Number n = (Number) object; 865 if (type.isPrimitive()) { 866 if (type == byte.class) { 867 return new Byte(n.byteValue()); 868 } 869 if (type == char.class) { 870 return new Character((char) n.intValue()); 871 } 872 if (type == short.class) { 873 return new Short(n.shortValue()); 874 } 875 if (type == int.class) { 876 return new Integer(n.intValue()); 877 } 878 if (type == long.class) { 879 return new Long(n.longValue()); 880 } 881 if (type == float.class) { 882 return new Float(n.floatValue()); 883 } 884 if (type == double.class) { 885 Double answer = new Double(n.doubleValue()); 886 //throw a runtime exception if conversion would be out-of-range for the type. 887 if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY 888 || answer.doubleValue() == Double.POSITIVE_INFINITY)) { 889 throw new GroovyRuntimeException("Automatic coercion of " + n.getClass().getName() 890 + " value " + n + " to double failed. Value is out of range."); 891 } 892 return answer; 893 } 894 } 895 else { 896 if (Number.class.isAssignableFrom(type)) { 897 if (type == Byte.class) { 898 return new Byte(n.byteValue()); 899 } 900 if (type == Character.class) { 901 return new Character((char) n.intValue()); 902 } 903 if (type == Short.class) { 904 return new Short(n.shortValue()); 905 } 906 if (type == Integer.class) { 907 return new Integer(n.intValue()); 908 } 909 if (type == Long.class) { 910 return new Long(n.longValue()); 911 } 912 if (type == Float.class) { 913 return new Float(n.floatValue()); 914 } 915 if (type == Double.class) { 916 Double answer = new Double(n.doubleValue()); 917 //throw a runtime exception if conversion would be out-of-range for the type. 918 if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY 919 || answer.doubleValue() == Double.POSITIVE_INFINITY)) { 920 throw new GroovyRuntimeException("Automatic coercion of " + n.getClass().getName() 921 + " value " + n + " to double failed. Value is out of range."); 922 } 923 return answer; 924 } 925 926 } 927 } 928 } 929 if (type == Boolean.class) { 930 return asBool(object) ? Boolean.TRUE : Boolean.FALSE; 931 } 932 Object[] args = null; 933 if (object instanceof Collection) { 934 Collection list = (Collection) object; 935 args = list.toArray(); 936 } 937 else if (object instanceof Object[]) { 938 args = (Object[]) object; 939 } 940 if (args != null) { 941 // lets try invoke the constructor with the list as arguments 942 // such as for creating a Dimension, Point, Color etc. 943 try { 944 return invokeConstructorOf(type, args); 945 } 946 catch (Exception e) { 947 // lets ignore exception and return the original object 948 // as the caller has more context to be able to throw a more 949 // meaningful exception 950 } 951 952 } 953 return object; 954 } 955 956 public Object asArray(Object object, Class type) { 957 Collection list = asCollection(object); 958 int size = list.size(); 959 Class elementType = type.getComponentType(); 960 Object array = Array.newInstance(elementType, size); 961 int idx = 0; 962 963 if (boolean.class.equals(elementType)) { 964 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { 965 Object element = iter.next(); 966 Array.setBoolean(array, idx, asBool(element)); 967 } 968 } 969 else if (byte.class.equals(elementType)) { 970 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { 971 Object element = iter.next(); 972 Array.setByte(array, idx, asByte(element)); 973 } 974 } 975 else if (char.class.equals(elementType)) { 976 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { 977 Object element = iter.next(); 978 Array.setChar(array, idx, asChar(element)); 979 } 980 } 981 else if (double.class.equals(elementType)) { 982 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { 983 Object element = iter.next(); 984 Array.setDouble(array, idx, asDouble(element)); 985 } 986 } 987 else if (float.class.equals(elementType)) { 988 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { 989 Object element = iter.next(); 990 Array.setFloat(array, idx, asFloat(element)); 991 } 992 } 993 else if (int.class.equals(elementType)) { 994 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { 995 Object element = iter.next(); 996 Array.setInt(array, idx, asInt(element)); 997 } 998 } 999 else if (long.class.equals(elementType)) { 1000 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { 1001 Object element = iter.next(); 1002 Array.setLong(array, idx, asLong(element)); 1003 } 1004 } 1005 else if (short.class.equals(elementType)) { 1006 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { 1007 Object element = iter.next(); 1008 Array.setShort(array, idx, asShort(element)); 1009 } 1010 } 1011 else { 1012 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { 1013 Object element = iter.next(); 1014 Object coercedElement = asType(element, elementType); 1015 Array.set(array, idx, coercedElement); 1016 } 1017 } 1018 return array; 1019 } 1020 1021 public Number asNumber(Object value) { 1022 if (value instanceof Number) { 1023 return (Number) value; 1024 } 1025 else if (value instanceof String) { 1026 String s = (String) value; 1027 1028 if (s.length() == 1) { 1029 return new Integer(s.charAt(0)); 1030 } 1031 else { 1032 return new BigDecimal(s); 1033 } 1034 } 1035 else if (value instanceof Character) { 1036 return new Integer(((Character) value).charValue()); 1037 } 1038 else { 1039 throw new GroovyRuntimeException("Could not convert object: " + value + " into a Number"); 1040 } 1041 } 1042 1043 public byte asByte(Object element) { 1044 return asNumber(element).byteValue(); 1045 } 1046 1047 public char asChar(Object element) { 1048 if (element instanceof String) { 1049 return asCharacter((String) element).charValue(); 1050 } 1051 return asCharacter(asNumber(element)).charValue(); 1052 } 1053 1054 public float asFloat(Object element) { 1055 return asNumber(element).floatValue(); 1056 } 1057 1058 public double asDouble(Object element) { 1059 return asNumber(element).doubleValue(); 1060 } 1061 1062 public short asShort(Object element) { 1063 return asNumber(element).shortValue(); 1064 } 1065 1066 public int asInt(Object element) { 1067 return asNumber(element).intValue(); 1068 } 1069 1070 public long asLong(Object element) { 1071 return asNumber(element).longValue(); 1072 } 1073 1074 public boolean asBool(Object object) { 1075 /* 1076 if (object instanceof Boolean) { 1077 Boolean booleanValue = (Boolean) object; 1078 return booleanValue.booleanValue(); 1079 } 1080 else if (object instanceof Matcher) { 1081 Matcher matcher = (Matcher) object; 1082 RegexSupport.setLastMatcher(matcher); 1083 return matcher.find(); 1084 } 1085 else if (object instanceof Collection) { 1086 Collection collection = (Collection) object; 1087 return !collection.isEmpty(); 1088 } 1089 else if (object instanceof String) { 1090 String string = (String) object; 1091 return string.length() > 0; 1092 } 1093 else if (object instanceof Number) { 1094 Number n = (Number) object; 1095 return n.doubleValue() != 0; 1096 } 1097 else { 1098 return object != null; 1099 } 1100 */ 1101 return AsmClassGenerator.asBool(object); 1102 } 1103 1104 protected Character asCharacter(Number value) { 1105 return new Character((char) value.intValue()); 1106 } 1107 1108 protected Character asCharacter(String text) { 1109 return new Character(text.charAt(0)); 1110 } 1111 1112 /** 1113 * @return true if the given value is a valid character string (i.e. has length of 1) 1114 */ 1115 protected boolean isValidCharacterString(Object value) { 1116 if (value instanceof String) { 1117 String s = (String) value; 1118 if (s.length() == 1) { 1119 return true; 1120 } 1121 } 1122 return false; 1123 } 1124 1125 public void removeMetaClass(Class clazz) { 1126 getMetaRegistry().removeMetaClass(clazz); 1127 } 1128 }