001    /*
002     $Id: Invoker.java,v 1.76 2005/06/08 20:06:19 glaforge Exp $
003    
004     Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005    
006     Redistribution and use of this software and associated documentation
007     ("Software"), with or without modification, are permitted provided
008     that the following conditions are met:
009    
010     1. Redistributions of source code must retain copyright
011        statements and notices.  Redistributions must also contain a
012        copy of this document.
013    
014     2. Redistributions in binary form must reproduce the
015        above copyright notice, this list of conditions and the
016        following disclaimer in the documentation and/or other
017        materials provided with the distribution.
018    
019     3. The name "groovy" must not be used to endorse or promote
020        products derived from this Software without prior written
021        permission of The Codehaus.  For written permission,
022        please contact info@codehaus.org.
023    
024     4. Products derived from this Software may not be called "groovy"
025        nor may "groovy" appear in their names without prior written
026        permission of The Codehaus. "groovy" is a registered
027        trademark of The Codehaus.
028    
029     5. Due credit should be given to The Codehaus -
030        http://groovy.codehaus.org/
031    
032     THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033     ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034     NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035     FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
036     THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037     INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041     STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043     OF THE POSSIBILITY OF SUCH DAMAGE.
044    
045     */
046    package org.codehaus.groovy.runtime;
047    
048    import org.codehaus.groovy.classgen.AsmClassGenerator;
049    import groovy.lang.Closure;
050    import groovy.lang.GroovyObject;
051    import groovy.lang.GroovyRuntimeException;
052    import groovy.lang.MetaClass;
053    import groovy.lang.MetaClassRegistry;
054    import groovy.lang.MissingMethodException;
055    import groovy.lang.Range;
056    import groovy.lang.SpreadList;
057    import groovy.lang.Tuple;
058    import groovy.lang.GroovyInterceptable;
059    import org.apache.xml.serialize.OutputFormat;
060    import org.apache.xml.serialize.XMLSerializer;
061    import org.w3c.dom.Element;
062    import org.w3c.dom.Node;
063    import org.w3c.dom.NodeList;
064    
065    import java.io.File;
066    import java.io.IOException;
067    import java.io.StringWriter;
068    import java.lang.reflect.Array;
069    import java.lang.reflect.Method;
070    import java.math.BigDecimal;
071    import java.security.AccessController;
072    import java.security.PrivilegedAction;
073    import java.util.ArrayList;
074    import java.util.Arrays;
075    import java.util.Collection;
076    import java.util.Collections;
077    import java.util.Enumeration;
078    import java.util.Iterator;
079    import java.util.List;
080    import java.util.Map;
081    import java.util.NoSuchElementException;
082    import java.util.regex.Matcher;
083    import java.util.regex.Pattern;
084    
085    /**
086     * A helper class to invoke methods or extract properties on arbitrary Java objects dynamically
087     *
088     * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
089     * @version $Revision: 1.76 $
090     */
091    public class Invoker {
092    
093        protected static final Object[] EMPTY_ARGUMENTS = {
094        };
095        protected static final Class[] EMPTY_TYPES = {
096        };
097    
098        public MetaClassRegistry getMetaRegistry() {
099            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            return format(arguments, false);
519        }
520    
521        /**
522         * A helper method to format the arguments types as a comma-separated list
523         */
524        public String toTypeString(Object[] arguments) {
525            if (arguments == null) {
526                return "null";
527            }
528            StringBuffer argBuf = new StringBuffer();
529            for (int i = 0; i < arguments.length; i++) {
530                if (i > 0) {
531                    argBuf.append(", ");
532                }
533                argBuf.append(arguments[i] != null ? arguments[i].getClass().getName() : "null");
534            }
535            return argBuf.toString();
536        }
537    
538        protected String format(Object arguments, boolean verbose) {
539            if (arguments == null) {
540                return "null";
541            }
542            else if (arguments.getClass().isArray()) {
543                return format(asCollection(arguments), verbose);
544            }
545            else if (arguments instanceof Range) {
546                Range range = (Range) arguments;
547                if (verbose) {
548                    return range.inspect();
549                }
550                else {
551                    return range.toString();
552                }
553            }
554            else if (arguments instanceof List) {
555                List list = (List) arguments;
556                StringBuffer buffer = new StringBuffer("[");
557                boolean first = true;
558                for (Iterator iter = list.iterator(); iter.hasNext();) {
559                    if (first) {
560                        first = false;
561                    }
562                    else {
563                        buffer.append(", ");
564                    }
565                    buffer.append(format(iter.next(), verbose));
566                }
567                buffer.append("]");
568                return buffer.toString();
569            }
570            else if (arguments instanceof Map) {
571                Map map = (Map) arguments;
572                if (map.isEmpty()) {
573                    return "[:]";
574                }
575                StringBuffer buffer = new StringBuffer("[");
576                boolean first = true;
577                for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
578                    if (first) {
579                        first = false;
580                    }
581                    else {
582                        buffer.append(", ");
583                    }
584                    Map.Entry entry = (Map.Entry) iter.next();
585                    buffer.append(format(entry.getKey(), verbose));
586                    buffer.append(":");
587                    buffer.append(format(entry.getValue(), verbose));
588                }
589                buffer.append("]");
590                return buffer.toString();
591            }
592            else if (arguments instanceof Element) {
593                Element node = (Element) arguments;
594                OutputFormat format = new OutputFormat(node.getOwnerDocument());
595                format.setOmitXMLDeclaration(true);
596                format.setIndenting(true);
597                format.setLineWidth(0);
598                format.setPreserveSpace(true);
599                StringWriter sw = new StringWriter();
600                XMLSerializer serializer = new XMLSerializer(sw, format);
601                try {
602                    serializer.asDOMSerializer();
603                    serializer.serialize(node);
604                }
605                catch (IOException e) {
606                }
607                return sw.toString();
608            }
609            else if (arguments instanceof String) {
610                if (verbose) {
611                    return "\"" + arguments + "\"";
612                }
613                else {
614                    return (String) arguments;
615                }
616            }
617            else {
618                return arguments.toString();
619            }
620        }
621    
622        /**
623         * Looks up the given property of the given object
624         */
625        public Object getProperty(Object object, String property) {
626            if (object == null) {
627                throw new NullPointerException("Cannot get property: " + property + " on null object");
628            }
629            else if (object instanceof GroovyObject) {
630                GroovyObject pogo = (GroovyObject) object;
631                return pogo.getProperty(property);
632            }
633            else if (object instanceof Map) {
634                Map map = (Map) object;
635                return map.get(property);
636            }
637            else {
638                return metaRegistry.getMetaClass(object.getClass()).getProperty(object, property);
639            }
640        }
641    
642        /**
643         * Sets the property on the given object
644         */
645        public void setProperty(Object object, String property, Object newValue) {
646            if (object == null) {
647                throw new GroovyRuntimeException("Cannot set property on null object");
648            }
649            else if (object instanceof GroovyObject) {
650                GroovyObject pogo = (GroovyObject) object;
651                pogo.setProperty(property, newValue);
652            }
653            else if (object instanceof Map) {
654                Map map = (Map) object;
655                map.put(property, newValue);
656            }
657            else {
658                metaRegistry.getMetaClass(object.getClass()).setProperty(object, property, newValue);
659            }
660        }
661    
662        /**
663         * Looks up the given attribute (field) on the given object
664         */
665        public Object getAttribute(Object object, String attribute) {
666            if (object == null) {
667                throw new NullPointerException("Cannot get attribute: " + attribute + " on null object");
668    
669                /**
670                 } else if (object instanceof GroovyObject) {
671                 GroovyObject pogo = (GroovyObject) object;
672                 return pogo.getAttribute(attribute);
673                 } else if (object instanceof Map) {
674                 Map map = (Map) object;
675                 return map.get(attribute);
676                 */
677            }
678            else {
679                return metaRegistry.getMetaClass(object.getClass()).getAttribute(object, attribute);
680            }
681        }
682    
683        /**
684         * Sets the given attribute (field) on the given object
685         */
686        public void setAttribute(Object object, String attribute, Object newValue) {
687            if (object == null) {
688                throw new GroovyRuntimeException("Cannot set attribute on null object");
689                /*
690            } else if (object instanceof GroovyObject) {
691                GroovyObject pogo = (GroovyObject) object;
692                pogo.setProperty(attribute, newValue);
693            } else if (object instanceof Map) {
694                Map map = (Map) object;
695                map.put(attribute, newValue);
696                */
697            }
698            else {
699                metaRegistry.getMetaClass(object.getClass()).setAttribute(object, attribute, newValue);
700            }
701        }
702    
703        /**
704         * Returns the method pointer for the given object name
705         */
706        public Closure getMethodPointer(Object object, String methodName) {
707            if (object == null) {
708                throw new NullPointerException("Cannot access method pointer for '" + methodName + "' on null object");
709            }
710            return metaRegistry.getMetaClass(object.getClass()).getMethodPointer(object, methodName);
711        }
712    
713    
714        /**
715         * Attempts to load the given class via name using the current class loader
716         * for this code or the thread context class loader
717         */
718        protected Class loadClass(String type) {
719            try {
720                return getClass().getClassLoader().loadClass(type);
721            }
722            catch (ClassNotFoundException e) {
723                try {
724                    return Thread.currentThread().getContextClassLoader().loadClass(type);
725                }
726                catch (ClassNotFoundException e2) {
727                    try {
728                        return Class.forName(type);
729                    }
730                    catch (ClassNotFoundException e3) {
731                    }
732                }
733                throw new GroovyRuntimeException("Could not load type: " + type, e);
734            }
735        }
736    
737        /**
738         * Find the right hand regex within the left hand string and return a matcher.
739         *
740         * @param left  string to compare
741         * @param right regular expression to compare the string to
742         * @return
743         */
744        public Matcher objectFindRegex(Object left, Object right) {
745            String stringToCompare;
746            if (left instanceof String) {
747                stringToCompare = (String) left;
748            }
749            else {
750                stringToCompare = toString(left);
751            }
752            String regexToCompareTo;
753            if (right instanceof String) {
754                regexToCompareTo = (String) right;
755            }
756            else if (right instanceof Pattern) {
757                Pattern pattern = (Pattern) right;
758                return pattern.matcher(stringToCompare);
759            }
760            else {
761                regexToCompareTo = toString(right);
762            }
763            Matcher matcher = Pattern.compile(regexToCompareTo).matcher(stringToCompare);
764            return matcher;
765        }
766    
767        /**
768         * Find the right hand regex within the left hand string and return a matcher.
769         *
770         * @param left  string to compare
771         * @param right regular expression to compare the string to
772         * @return
773         */
774        public boolean objectMatchRegex(Object left, Object right) {
775            Pattern pattern;
776            if (right instanceof Pattern) {
777                pattern = (Pattern) right;
778            }
779            else {
780                pattern = Pattern.compile(toString(right));
781            }
782            String stringToCompare = toString(left);
783            Matcher matcher = pattern.matcher(stringToCompare);
784            RegexSupport.setLastMatcher(matcher);
785            return matcher.matches();
786        }
787    
788        /**
789         * Compile a regular expression from a string.
790         *
791         * @param regex
792         * @return
793         */
794        public Pattern regexPattern(Object regex) {
795            return Pattern.compile(regex.toString());
796        }
797    
798        public Object asType(Object object, Class type) {
799            if (object == null) {
800                return null;
801            }
802            // TODO we should move these methods to groovy method, like g$asType() so that
803            // we can use operator overloading to customize on a per-type basis
804            if (type.isArray()) {
805                return asArray(object, type);
806    
807            }
808            if (type.isInstance(object)) {
809                return object;
810            }
811            if (type.isAssignableFrom(Collection.class)) {
812                if (object.getClass().isArray()) {
813                    // lets call the collections constructor
814                    // passing in the list wrapper
815                    Collection answer = null;
816                    try {
817                        answer = (Collection) type.newInstance();
818                    }
819                    catch (Exception e) {
820                        throw new ClassCastException("Could not instantiate instance of: " + type.getName() + ". Reason: " + e);
821                    }
822    
823                    // we cannot just wrap in a List as we support primitive type arrays
824                    int length = Array.getLength(object);
825                    for (int i = 0; i < length; i++) {
826                        Object element = Array.get(object, i);
827                        answer.add(element);
828                    }
829                    return answer;
830                }
831            }
832            if (type.equals(String.class)) {
833                return object.toString();
834            }
835            if (type.equals(Character.class)) {
836                if (object instanceof Number) {
837                    return asCharacter((Number) object);
838                }
839                else {
840                    String text = object.toString();
841                    if (text.length() == 1) {
842                        return new Character(text.charAt(0));
843                    }
844                    else {
845                        throw new ClassCastException("Cannot cast: " + text + " to a Character");
846                    }
847                }
848            }
849            if (Number.class.isAssignableFrom(type)) {
850                if (object instanceof Character) {
851                    return new Integer(((Character) object).charValue());
852                }
853                else if (object instanceof String) {
854                    String c = (String) object;
855                    if (c.length() == 1) {
856                        return new Integer(c.charAt(0));
857                    }
858                    else {
859                        throw new ClassCastException("Cannot cast: '" + c + "' to an Integer");
860                    }
861                }
862            }
863            if (object instanceof Number) {
864                Number n = (Number) object;
865                if (type.isPrimitive()) {
866                    if (type == byte.class) {
867                        return new Byte(n.byteValue());
868                    }
869                    if (type == char.class) {
870                        return new Character((char) n.intValue());
871                    }
872                    if (type == short.class) {
873                        return new Short(n.shortValue());
874                    }
875                    if (type == int.class) {
876                        return new Integer(n.intValue());
877                    }
878                    if (type == long.class) {
879                        return new Long(n.longValue());
880                    }
881                    if (type == float.class) {
882                        return new Float(n.floatValue());
883                    }
884                    if (type == double.class) {
885                        Double answer = new Double(n.doubleValue());
886                        //throw a runtime exception if conversion would be out-of-range for the type.
887                        if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY
888                                || answer.doubleValue() == Double.POSITIVE_INFINITY)) {
889                            throw new GroovyRuntimeException("Automatic coercion of " + n.getClass().getName()
890                                    + " value " + n + " to double failed.  Value is out of range.");
891                        }
892                        return answer;
893                    }
894                }
895                else {
896                    if (Number.class.isAssignableFrom(type)) {
897                        if (type == Byte.class) {
898                            return new Byte(n.byteValue());
899                        }
900                        if (type == Character.class) {
901                            return new Character((char) n.intValue());
902                        }
903                        if (type == Short.class) {
904                            return new Short(n.shortValue());
905                        }
906                        if (type == Integer.class) {
907                            return new Integer(n.intValue());
908                        }
909                        if (type == Long.class) {
910                            return new Long(n.longValue());
911                        }
912                        if (type == Float.class) {
913                            return new Float(n.floatValue());
914                        }
915                        if (type == Double.class) {
916                            Double answer = new Double(n.doubleValue());
917                            //throw a runtime exception if conversion would be out-of-range for the type.
918                            if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY
919                                    || answer.doubleValue() == Double.POSITIVE_INFINITY)) {
920                                throw new GroovyRuntimeException("Automatic coercion of " + n.getClass().getName()
921                                        + " value " + n + " to double failed.  Value is out of range.");
922                            }
923                            return answer;
924                        }
925    
926                    }
927                }
928            }
929            if (type == Boolean.class) {
930                return asBool(object) ? Boolean.TRUE : Boolean.FALSE;
931            }
932            Object[] args = null;
933            if (object instanceof Collection) {
934                Collection list = (Collection) object;
935                args = list.toArray();
936            }
937            else if (object instanceof Object[]) {
938                args = (Object[]) object;
939            }
940            if (args != null) {
941                // lets try invoke the constructor with the list as arguments
942                // such as for creating a Dimension, Point, Color etc.
943                try {
944                    return invokeConstructorOf(type, args);
945                }
946                catch (Exception e) {
947                    // lets ignore exception and return the original object
948                    // as the caller has more context to be able to throw a more
949                    // meaningful exception
950                }
951    
952            }
953            return object;
954        }
955    
956        public Object asArray(Object object, Class type) {
957            Collection list = asCollection(object);
958            int size = list.size();
959            Class elementType = type.getComponentType();
960            Object array = Array.newInstance(elementType, size);
961            int idx = 0;
962    
963            if (boolean.class.equals(elementType)) {
964                for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
965                    Object element = iter.next();
966                    Array.setBoolean(array, idx, asBool(element));
967                }
968            }
969            else if (byte.class.equals(elementType)) {
970                for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
971                    Object element = iter.next();
972                    Array.setByte(array, idx, asByte(element));
973                }
974            }
975            else if (char.class.equals(elementType)) {
976                for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
977                    Object element = iter.next();
978                    Array.setChar(array, idx, asChar(element));
979                }
980            }
981            else if (double.class.equals(elementType)) {
982                for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
983                    Object element = iter.next();
984                    Array.setDouble(array, idx, asDouble(element));
985                }
986            }
987            else if (float.class.equals(elementType)) {
988                for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
989                    Object element = iter.next();
990                    Array.setFloat(array, idx, asFloat(element));
991                }
992            }
993            else if (int.class.equals(elementType)) {
994                for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
995                    Object element = iter.next();
996                    Array.setInt(array, idx, asInt(element));
997                }
998            }
999            else if (long.class.equals(elementType)) {
1000                for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
1001                    Object element = iter.next();
1002                    Array.setLong(array, idx, asLong(element));
1003                }
1004            }
1005            else if (short.class.equals(elementType)) {
1006                for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
1007                    Object element = iter.next();
1008                    Array.setShort(array, idx, asShort(element));
1009                }
1010            }
1011            else {
1012                for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
1013                    Object element = iter.next();
1014                    Object coercedElement = asType(element, elementType);
1015                    Array.set(array, idx, coercedElement);
1016                }
1017            }
1018            return array;
1019        }
1020    
1021        public Number asNumber(Object value) {
1022            if (value instanceof Number) {
1023                return (Number) value;
1024            }
1025            else if (value instanceof String) {
1026                String s = (String) value;
1027    
1028                if (s.length() == 1) {
1029                    return new Integer(s.charAt(0));
1030                }
1031                else {
1032                    return new BigDecimal(s);
1033                }
1034            }
1035            else if (value instanceof Character) {
1036                return new Integer(((Character) value).charValue());
1037            }
1038            else {
1039                throw new GroovyRuntimeException("Could not convert object: " + value + " into a Number");
1040            }
1041        }
1042    
1043        public byte asByte(Object element) {
1044            return asNumber(element).byteValue();
1045        }
1046    
1047        public char asChar(Object element) {
1048            if (element instanceof String) {
1049                return asCharacter((String) element).charValue();
1050            }
1051            return asCharacter(asNumber(element)).charValue();
1052        }
1053    
1054        public float asFloat(Object element) {
1055            return asNumber(element).floatValue();
1056        }
1057    
1058        public double asDouble(Object element) {
1059            return asNumber(element).doubleValue();
1060        }
1061    
1062        public short asShort(Object element) {
1063            return asNumber(element).shortValue();
1064        }
1065    
1066        public int asInt(Object element) {
1067            return asNumber(element).intValue();
1068        }
1069    
1070        public long asLong(Object element) {
1071            return asNumber(element).longValue();
1072        }
1073    
1074        public boolean asBool(Object object) {
1075            /*
1076            if (object instanceof Boolean) {
1077                Boolean booleanValue = (Boolean) object;
1078                return booleanValue.booleanValue();
1079            }
1080            else if (object instanceof Matcher) {
1081                Matcher matcher = (Matcher) object;
1082                RegexSupport.setLastMatcher(matcher);
1083                return matcher.find();
1084            }
1085            else if (object instanceof Collection) {
1086                Collection collection = (Collection) object;
1087                return !collection.isEmpty();
1088            }
1089            else if (object instanceof String) {
1090                String string = (String) object;
1091                return string.length() > 0;
1092            }
1093            else if (object instanceof Number) {
1094                Number n = (Number) object;
1095                return n.doubleValue() != 0;
1096            }
1097            else {
1098                return object != null;
1099            }
1100            */
1101            return AsmClassGenerator.asBool(object);
1102        }
1103    
1104        protected Character asCharacter(Number value) {
1105            return new Character((char) value.intValue());
1106        }
1107    
1108        protected Character asCharacter(String text) {
1109            return new Character(text.charAt(0));
1110        }
1111    
1112        /**
1113         * @return true if the given value is a valid character string (i.e. has length of 1)
1114         */
1115        protected boolean isValidCharacterString(Object value) {
1116            if (value instanceof String) {
1117                String s = (String) value;
1118                if (s.length() == 1) {
1119                    return true;
1120                }
1121            }
1122            return false;
1123        }
1124    
1125        public void removeMetaClass(Class clazz) {
1126            getMetaRegistry().removeMetaClass(clazz);
1127        }
1128    }