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 }