View Javadoc

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