View Javadoc

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