View Javadoc

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