View Javadoc

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