001 /* 002 $Id: Invoker.java,v 1.77 2005/07/23 12:09:47 phk 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.77 $ 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 if (arguments instanceof Object[]) 519 return toArrayString((Object[]) arguments); 520 else if (arguments instanceof Map) 521 return toMapString((Map) arguments); 522 else if (arguments instanceof Collection) 523 return format(arguments, true); 524 else 525 return format(arguments, false); 526 } 527 528 /** 529 * A helper method to format the arguments types as a comma-separated list 530 */ 531 public String toTypeString(Object[] arguments) { 532 if (arguments == null) { 533 return "null"; 534 } 535 StringBuffer argBuf = new StringBuffer(); 536 for (int i = 0; i < arguments.length; i++) { 537 if (i > 0) { 538 argBuf.append(", "); 539 } 540 argBuf.append(arguments[i] != null ? arguments[i].getClass().getName() : "null"); 541 } 542 return argBuf.toString(); 543 } 544 545 /** 546 * A helper method to return the string representation of a map with bracket boundaries "[" and "]". 547 */ 548 public String toMapString(Map arg) { 549 if (arg == null) { 550 return "null"; 551 } 552 if (arg.isEmpty()) { 553 return "[:]"; 554 } 555 String sbdry = "["; 556 String ebdry = "]"; 557 StringBuffer buffer = new StringBuffer(sbdry); 558 boolean first = true; 559 for (Iterator iter = arg.entrySet().iterator(); iter.hasNext();) { 560 if (first) 561 first = false; 562 else 563 buffer.append(", "); 564 Map.Entry entry = (Map.Entry) iter.next(); 565 buffer.append(format(entry.getKey(), true)); 566 buffer.append(":"); 567 buffer.append(format(entry.getValue(), true)); 568 } 569 buffer.append(ebdry); 570 return buffer.toString(); 571 } 572 573 /** 574 * A helper method to return the string representation of a list with bracket boundaries "[" and "]". 575 */ 576 public String toListString(Collection arg) { 577 if (arg == null) { 578 return "null"; 579 } 580 if (arg.isEmpty()) { 581 return "[]"; 582 } 583 String sbdry = "["; 584 String ebdry = "]"; 585 StringBuffer buffer = new StringBuffer(sbdry); 586 boolean first = true; 587 for (Iterator iter = arg.iterator(); iter.hasNext();) { 588 if (first) 589 first = false; 590 else 591 buffer.append(", "); 592 Object elem = iter.next(); 593 buffer.append(format(elem, true)); 594 } 595 buffer.append(ebdry); 596 return buffer.toString(); 597 } 598 599 /** 600 * A helper method to return the string representation of an arrray of objects 601 * with brace boundaries "{" and "}". 602 */ 603 public String toArrayString(Object[] arguments) { 604 if (arguments == null) { 605 return "null"; 606 } 607 String sbdry = "{"; 608 String ebdry = "}"; 609 StringBuffer argBuf = new StringBuffer(sbdry); 610 for (int i = 0; i < arguments.length; i++) { 611 if (i > 0) { 612 argBuf.append(", "); 613 } 614 argBuf.append(format(arguments[i], true)); 615 } 616 argBuf.append(ebdry); 617 return argBuf.toString(); 618 } 619 620 protected String format(Object arguments, boolean verbose) { 621 if (arguments == null) { 622 return "null"; 623 } 624 else if (arguments.getClass().isArray()) { 625 return format(asCollection(arguments), verbose); 626 } 627 else if (arguments instanceof Range) { 628 Range range = (Range) arguments; 629 if (verbose) { 630 return range.inspect(); 631 } 632 else { 633 return range.toString(); 634 } 635 } 636 else if (arguments instanceof List) { 637 List list = (List) arguments; 638 StringBuffer buffer = new StringBuffer("["); 639 boolean first = true; 640 for (Iterator iter = list.iterator(); iter.hasNext();) { 641 if (first) { 642 first = false; 643 } 644 else { 645 buffer.append(", "); 646 } 647 buffer.append(format(iter.next(), verbose)); 648 } 649 buffer.append("]"); 650 return buffer.toString(); 651 } 652 else if (arguments instanceof Map) { 653 Map map = (Map) arguments; 654 if (map.isEmpty()) { 655 return "[:]"; 656 } 657 StringBuffer buffer = new StringBuffer("["); 658 boolean first = true; 659 for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) { 660 if (first) { 661 first = false; 662 } 663 else { 664 buffer.append(", "); 665 } 666 Map.Entry entry = (Map.Entry) iter.next(); 667 buffer.append(format(entry.getKey(), verbose)); 668 buffer.append(":"); 669 buffer.append(format(entry.getValue(), verbose)); 670 } 671 buffer.append("]"); 672 return buffer.toString(); 673 } 674 else if (arguments instanceof Element) { 675 Element node = (Element) arguments; 676 OutputFormat format = new OutputFormat(node.getOwnerDocument()); 677 format.setOmitXMLDeclaration(true); 678 format.setIndenting(true); 679 format.setLineWidth(0); 680 format.setPreserveSpace(true); 681 StringWriter sw = new StringWriter(); 682 XMLSerializer serializer = new XMLSerializer(sw, format); 683 try { 684 serializer.asDOMSerializer(); 685 serializer.serialize(node); 686 } 687 catch (IOException e) { 688 } 689 return sw.toString(); 690 } 691 else if (arguments instanceof String) { 692 if (verbose) { 693 String arg = ((String)arguments).replaceAll("\\n", "\\\\n"); // line feed 694 arg = arg.replaceAll("\\r", "\\\\r"); // carriage return 695 arg = arg.replaceAll("\\t", "\\\\t"); // tab 696 arg = arg.replaceAll("\\f", "\\\\f"); // form feed 697 arg = arg.replaceAll("\\\"", "\\\\\""); // double quotation amrk 698 arg = arg.replaceAll("\\\\", "\\\\"); // back slash 699 return "\"" + arg + "\""; 700 } 701 else { 702 return (String) arguments; 703 } 704 } 705 else { 706 return arguments.toString(); 707 } 708 } 709 710 /** 711 * Looks up the given property of the given object 712 */ 713 public Object getProperty(Object object, String property) { 714 if (object == null) { 715 throw new NullPointerException("Cannot get property: " + property + " on null object"); 716 } 717 else if (object instanceof GroovyObject) { 718 GroovyObject pogo = (GroovyObject) object; 719 return pogo.getProperty(property); 720 } 721 else if (object instanceof Map) { 722 Map map = (Map) object; 723 return map.get(property); 724 } 725 else { 726 return metaRegistry.getMetaClass(object.getClass()).getProperty(object, property); 727 } 728 } 729 730 /** 731 * Sets the property on the given object 732 */ 733 public void setProperty(Object object, String property, Object newValue) { 734 if (object == null) { 735 throw new GroovyRuntimeException("Cannot set property on null object"); 736 } 737 else if (object instanceof GroovyObject) { 738 GroovyObject pogo = (GroovyObject) object; 739 pogo.setProperty(property, newValue); 740 } 741 else if (object instanceof Map) { 742 Map map = (Map) object; 743 map.put(property, newValue); 744 } 745 else { 746 metaRegistry.getMetaClass(object.getClass()).setProperty(object, property, newValue); 747 } 748 } 749 750 /** 751 * Looks up the given attribute (field) on the given object 752 */ 753 public Object getAttribute(Object object, String attribute) { 754 if (object == null) { 755 throw new NullPointerException("Cannot get attribute: " + attribute + " on null object"); 756 757 /** 758 } else if (object instanceof GroovyObject) { 759 GroovyObject pogo = (GroovyObject) object; 760 return pogo.getAttribute(attribute); 761 } else if (object instanceof Map) { 762 Map map = (Map) object; 763 return map.get(attribute); 764 */ 765 } 766 else { 767 return metaRegistry.getMetaClass(object.getClass()).getAttribute(object, attribute); 768 } 769 } 770 771 /** 772 * Sets the given attribute (field) on the given object 773 */ 774 public void setAttribute(Object object, String attribute, Object newValue) { 775 if (object == null) { 776 throw new GroovyRuntimeException("Cannot set attribute on null object"); 777 /* 778 } else if (object instanceof GroovyObject) { 779 GroovyObject pogo = (GroovyObject) object; 780 pogo.setProperty(attribute, newValue); 781 } else if (object instanceof Map) { 782 Map map = (Map) object; 783 map.put(attribute, newValue); 784 */ 785 } 786 else { 787 metaRegistry.getMetaClass(object.getClass()).setAttribute(object, attribute, newValue); 788 } 789 } 790 791 /** 792 * Returns the method pointer for the given object name 793 */ 794 public Closure getMethodPointer(Object object, String methodName) { 795 if (object == null) { 796 throw new NullPointerException("Cannot access method pointer for '" + methodName + "' on null object"); 797 } 798 return metaRegistry.getMetaClass(object.getClass()).getMethodPointer(object, methodName); 799 } 800 801 802 /** 803 * Attempts to load the given class via name using the current class loader 804 * for this code or the thread context class loader 805 */ 806 protected Class loadClass(String type) { 807 try { 808 return getClass().getClassLoader().loadClass(type); 809 } 810 catch (ClassNotFoundException e) { 811 try { 812 return Thread.currentThread().getContextClassLoader().loadClass(type); 813 } 814 catch (ClassNotFoundException e2) { 815 try { 816 return Class.forName(type); 817 } 818 catch (ClassNotFoundException e3) { 819 } 820 } 821 throw new GroovyRuntimeException("Could not load type: " + type, e); 822 } 823 } 824 825 /** 826 * Find the right hand regex within the left hand string and return a matcher. 827 * 828 * @param left string to compare 829 * @param right regular expression to compare the string to 830 * @return 831 */ 832 public Matcher objectFindRegex(Object left, Object right) { 833 String stringToCompare; 834 if (left instanceof String) { 835 stringToCompare = (String) left; 836 } 837 else { 838 stringToCompare = toString(left); 839 } 840 String regexToCompareTo; 841 if (right instanceof String) { 842 regexToCompareTo = (String) right; 843 } 844 else if (right instanceof Pattern) { 845 Pattern pattern = (Pattern) right; 846 return pattern.matcher(stringToCompare); 847 } 848 else { 849 regexToCompareTo = toString(right); 850 } 851 Matcher matcher = Pattern.compile(regexToCompareTo).matcher(stringToCompare); 852 return matcher; 853 } 854 855 /** 856 * Find the right hand regex within the left hand string and return a matcher. 857 * 858 * @param left string to compare 859 * @param right regular expression to compare the string to 860 * @return 861 */ 862 public boolean objectMatchRegex(Object left, Object right) { 863 Pattern pattern; 864 if (right instanceof Pattern) { 865 pattern = (Pattern) right; 866 } 867 else { 868 pattern = Pattern.compile(toString(right)); 869 } 870 String stringToCompare = toString(left); 871 Matcher matcher = pattern.matcher(stringToCompare); 872 RegexSupport.setLastMatcher(matcher); 873 return matcher.matches(); 874 } 875 876 /** 877 * Compile a regular expression from a string. 878 * 879 * @param regex 880 * @return 881 */ 882 public Pattern regexPattern(Object regex) { 883 return Pattern.compile(regex.toString()); 884 } 885 886 public Object asType(Object object, Class type) { 887 if (object == null) { 888 return null; 889 } 890 // TODO we should move these methods to groovy method, like g$asType() so that 891 // we can use operator overloading to customize on a per-type basis 892 if (type.isArray()) { 893 return asArray(object, type); 894 895 } 896 if (type.isInstance(object)) { 897 return object; 898 } 899 if (type.isAssignableFrom(Collection.class)) { 900 if (object.getClass().isArray()) { 901 // lets call the collections constructor 902 // passing in the list wrapper 903 Collection answer = null; 904 try { 905 answer = (Collection) type.newInstance(); 906 } 907 catch (Exception e) { 908 throw new ClassCastException("Could not instantiate instance of: " + type.getName() + ". Reason: " + e); 909 } 910 911 // we cannot just wrap in a List as we support primitive type arrays 912 int length = Array.getLength(object); 913 for (int i = 0; i < length; i++) { 914 Object element = Array.get(object, i); 915 answer.add(element); 916 } 917 return answer; 918 } 919 } 920 if (type.equals(String.class)) { 921 return object.toString(); 922 } 923 if (type.equals(Character.class)) { 924 if (object instanceof Number) { 925 return asCharacter((Number) object); 926 } 927 else { 928 String text = object.toString(); 929 if (text.length() == 1) { 930 return new Character(text.charAt(0)); 931 } 932 else { 933 throw new ClassCastException("Cannot cast: " + text + " to a Character"); 934 } 935 } 936 } 937 if (Number.class.isAssignableFrom(type)) { 938 if (object instanceof Character) { 939 return new Integer(((Character) object).charValue()); 940 } 941 else if (object instanceof String) { 942 String c = (String) object; 943 if (c.length() == 1) { 944 return new Integer(c.charAt(0)); 945 } 946 else { 947 throw new ClassCastException("Cannot cast: '" + c + "' to an Integer"); 948 } 949 } 950 } 951 if (object instanceof Number) { 952 Number n = (Number) object; 953 if (type.isPrimitive()) { 954 if (type == byte.class) { 955 return new Byte(n.byteValue()); 956 } 957 if (type == char.class) { 958 return new Character((char) n.intValue()); 959 } 960 if (type == short.class) { 961 return new Short(n.shortValue()); 962 } 963 if (type == int.class) { 964 return new Integer(n.intValue()); 965 } 966 if (type == long.class) { 967 return new Long(n.longValue()); 968 } 969 if (type == float.class) { 970 return new Float(n.floatValue()); 971 } 972 if (type == double.class) { 973 Double answer = new Double(n.doubleValue()); 974 //throw a runtime exception if conversion would be out-of-range for the type. 975 if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY 976 || answer.doubleValue() == Double.POSITIVE_INFINITY)) { 977 throw new GroovyRuntimeException("Automatic coercion of " + n.getClass().getName() 978 + " value " + n + " to double failed. Value is out of range."); 979 } 980 return answer; 981 } 982 } 983 else { 984 if (Number.class.isAssignableFrom(type)) { 985 if (type == Byte.class) { 986 return new Byte(n.byteValue()); 987 } 988 if (type == Character.class) { 989 return new Character((char) n.intValue()); 990 } 991 if (type == Short.class) { 992 return new Short(n.shortValue()); 993 } 994 if (type == Integer.class) { 995 return new Integer(n.intValue()); 996 } 997 if (type == Long.class) { 998 return new Long(n.longValue()); 999 } 1000 if (type == Float.class) { 1001 return new Float(n.floatValue()); 1002 } 1003 if (type == Double.class) { 1004 Double answer = new Double(n.doubleValue()); 1005 //throw a runtime exception if conversion would be out-of-range for the type. 1006 if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY 1007 || answer.doubleValue() == Double.POSITIVE_INFINITY)) { 1008 throw new GroovyRuntimeException("Automatic coercion of " + n.getClass().getName() 1009 + " value " + n + " to double failed. Value is out of range."); 1010 } 1011 return answer; 1012 } 1013 1014 } 1015 } 1016 } 1017 if (type == Boolean.class) { 1018 return asBool(object) ? Boolean.TRUE : Boolean.FALSE; 1019 } 1020 Object[] args = null; 1021 if (object instanceof Collection) { 1022 Collection list = (Collection) object; 1023 args = list.toArray(); 1024 } 1025 else if (object instanceof Object[]) { 1026 args = (Object[]) object; 1027 } 1028 if (args != null) { 1029 // lets try invoke the constructor with the list as arguments 1030 // such as for creating a Dimension, Point, Color etc. 1031 try { 1032 return invokeConstructorOf(type, args); 1033 } 1034 catch (Exception e) { 1035 // lets ignore exception and return the original object 1036 // as the caller has more context to be able to throw a more 1037 // meaningful exception 1038 } 1039 1040 } 1041 return object; 1042 } 1043 1044 public Object asArray(Object object, Class type) { 1045 Collection list = asCollection(object); 1046 int size = list.size(); 1047 Class elementType = type.getComponentType(); 1048 Object array = Array.newInstance(elementType, size); 1049 int idx = 0; 1050 1051 if (boolean.class.equals(elementType)) { 1052 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { 1053 Object element = iter.next(); 1054 Array.setBoolean(array, idx, asBool(element)); 1055 } 1056 } 1057 else if (byte.class.equals(elementType)) { 1058 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { 1059 Object element = iter.next(); 1060 Array.setByte(array, idx, asByte(element)); 1061 } 1062 } 1063 else if (char.class.equals(elementType)) { 1064 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { 1065 Object element = iter.next(); 1066 Array.setChar(array, idx, asChar(element)); 1067 } 1068 } 1069 else if (double.class.equals(elementType)) { 1070 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { 1071 Object element = iter.next(); 1072 Array.setDouble(array, idx, asDouble(element)); 1073 } 1074 } 1075 else if (float.class.equals(elementType)) { 1076 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { 1077 Object element = iter.next(); 1078 Array.setFloat(array, idx, asFloat(element)); 1079 } 1080 } 1081 else if (int.class.equals(elementType)) { 1082 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { 1083 Object element = iter.next(); 1084 Array.setInt(array, idx, asInt(element)); 1085 } 1086 } 1087 else if (long.class.equals(elementType)) { 1088 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { 1089 Object element = iter.next(); 1090 Array.setLong(array, idx, asLong(element)); 1091 } 1092 } 1093 else if (short.class.equals(elementType)) { 1094 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { 1095 Object element = iter.next(); 1096 Array.setShort(array, idx, asShort(element)); 1097 } 1098 } 1099 else { 1100 for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { 1101 Object element = iter.next(); 1102 Object coercedElement = asType(element, elementType); 1103 Array.set(array, idx, coercedElement); 1104 } 1105 } 1106 return array; 1107 } 1108 1109 public Number asNumber(Object value) { 1110 if (value instanceof Number) { 1111 return (Number) value; 1112 } 1113 else if (value instanceof String) { 1114 String s = (String) value; 1115 1116 if (s.length() == 1) { 1117 return new Integer(s.charAt(0)); 1118 } 1119 else { 1120 return new BigDecimal(s); 1121 } 1122 } 1123 else if (value instanceof Character) { 1124 return new Integer(((Character) value).charValue()); 1125 } 1126 else { 1127 throw new GroovyRuntimeException("Could not convert object: " + value + " into a Number"); 1128 } 1129 } 1130 1131 public byte asByte(Object element) { 1132 return asNumber(element).byteValue(); 1133 } 1134 1135 public char asChar(Object element) { 1136 if (element instanceof String) { 1137 return asCharacter((String) element).charValue(); 1138 } 1139 return asCharacter(asNumber(element)).charValue(); 1140 } 1141 1142 public float asFloat(Object element) { 1143 return asNumber(element).floatValue(); 1144 } 1145 1146 public double asDouble(Object element) { 1147 return asNumber(element).doubleValue(); 1148 } 1149 1150 public short asShort(Object element) { 1151 return asNumber(element).shortValue(); 1152 } 1153 1154 public int asInt(Object element) { 1155 return asNumber(element).intValue(); 1156 } 1157 1158 public long asLong(Object element) { 1159 return asNumber(element).longValue(); 1160 } 1161 1162 public boolean asBool(Object object) { 1163 /* 1164 if (object instanceof Boolean) { 1165 Boolean booleanValue = (Boolean) object; 1166 return booleanValue.booleanValue(); 1167 } 1168 else if (object instanceof Matcher) { 1169 Matcher matcher = (Matcher) object; 1170 RegexSupport.setLastMatcher(matcher); 1171 return matcher.find(); 1172 } 1173 else if (object instanceof Collection) { 1174 Collection collection = (Collection) object; 1175 return !collection.isEmpty(); 1176 } 1177 else if (object instanceof String) { 1178 String string = (String) object; 1179 return string.length() > 0; 1180 } 1181 else if (object instanceof Number) { 1182 Number n = (Number) object; 1183 return n.doubleValue() != 0; 1184 } 1185 else { 1186 return object != null; 1187 } 1188 */ 1189 return AsmClassGenerator.asBool(object); 1190 } 1191 1192 protected Character asCharacter(Number value) { 1193 return new Character((char) value.intValue()); 1194 } 1195 1196 protected Character asCharacter(String text) { 1197 return new Character(text.charAt(0)); 1198 } 1199 1200 /** 1201 * @return true if the given value is a valid character string (i.e. has length of 1) 1202 */ 1203 protected boolean isValidCharacterString(Object value) { 1204 if (value instanceof String) { 1205 String s = (String) value; 1206 if (s.length() == 1) { 1207 return true; 1208 } 1209 } 1210 return false; 1211 } 1212 1213 public void removeMetaClass(Class clazz) { 1214 getMetaRegistry().removeMetaClass(clazz); 1215 } 1216 }