View Javadoc

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