001    /*
002     $Id: Invoker.java,v 1.77 2005/07/23 12:09:47 phk 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.77 $
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            if (arguments instanceof Object[])
519                return toArrayString((Object[]) arguments);
520            else if (arguments instanceof Map)
521                return toMapString((Map) arguments);
522            else if (arguments instanceof Collection)
523                return format(arguments, true);
524            else
525                return format(arguments, false);
526        }
527    
528        /**
529         * A helper method to format the arguments types as a comma-separated list
530         */
531        public String toTypeString(Object[] arguments) {
532            if (arguments == null) {
533                return "null";
534            }
535            StringBuffer argBuf = new StringBuffer();
536            for (int i = 0; i < arguments.length; i++) {
537                if (i > 0) {
538                    argBuf.append(", ");
539                }
540                argBuf.append(arguments[i] != null ? arguments[i].getClass().getName() : "null");
541            }
542            return argBuf.toString();
543        }
544    
545        /**
546         * A helper method to return the string representation of a map with bracket boundaries "[" and "]".
547         */
548        public String toMapString(Map arg) {
549            if (arg == null) {
550                return "null";
551            }
552            if (arg.isEmpty()) {
553                return "[:]";
554            }
555            String sbdry = "[";
556            String ebdry = "]";
557            StringBuffer buffer = new StringBuffer(sbdry);
558            boolean first = true;
559            for (Iterator iter = arg.entrySet().iterator(); iter.hasNext();) {
560                if (first)
561                    first = false;
562                else
563                    buffer.append(", ");
564                Map.Entry entry = (Map.Entry) iter.next();
565                buffer.append(format(entry.getKey(), true));
566                buffer.append(":");
567                buffer.append(format(entry.getValue(), true));
568            }
569            buffer.append(ebdry);
570            return buffer.toString();
571        }
572    
573        /**
574         * A helper method to return the string representation of a list with bracket boundaries "[" and "]".
575         */
576        public String toListString(Collection arg) {
577            if (arg == null) {
578                return "null";
579            }
580            if (arg.isEmpty()) {
581                return "[]";
582            }
583            String sbdry = "[";
584            String ebdry = "]";
585            StringBuffer buffer = new StringBuffer(sbdry);
586            boolean first = true;
587            for (Iterator iter = arg.iterator(); iter.hasNext();) {
588                if (first)
589                    first = false;
590                else
591                    buffer.append(", ");
592                Object elem = iter.next();
593                buffer.append(format(elem, true));
594            }
595            buffer.append(ebdry);
596            return buffer.toString();
597        }
598    
599        /**
600         * A helper method to return the string representation of an arrray of objects
601         * with brace boundaries "{" and "}".
602         */
603        public String toArrayString(Object[] arguments) {
604            if (arguments == null) {
605                return "null";
606            }
607            String sbdry = "{";
608            String ebdry = "}";
609            StringBuffer argBuf = new StringBuffer(sbdry);
610            for (int i = 0; i < arguments.length; i++) {
611                if (i > 0) {
612                    argBuf.append(", ");
613                }
614                argBuf.append(format(arguments[i], true));
615            }
616            argBuf.append(ebdry);
617            return argBuf.toString();
618        }
619    
620        protected String format(Object arguments, boolean verbose) {
621            if (arguments == null) {
622                return "null";
623            }
624            else if (arguments.getClass().isArray()) {
625                return format(asCollection(arguments), verbose);
626            }
627            else if (arguments instanceof Range) {
628                Range range = (Range) arguments;
629                if (verbose) {
630                    return range.inspect();
631                }
632                else {
633                    return range.toString();
634                }
635            }
636            else if (arguments instanceof List) {
637                List list = (List) arguments;
638                StringBuffer buffer = new StringBuffer("[");
639                boolean first = true;
640                for (Iterator iter = list.iterator(); iter.hasNext();) {
641                    if (first) {
642                        first = false;
643                    }
644                    else {
645                        buffer.append(", ");
646                    }
647                    buffer.append(format(iter.next(), verbose));
648                }
649                buffer.append("]");
650                return buffer.toString();
651            }
652            else if (arguments instanceof Map) {
653                Map map = (Map) arguments;
654                if (map.isEmpty()) {
655                    return "[:]";
656                }
657                StringBuffer buffer = new StringBuffer("[");
658                boolean first = true;
659                for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
660                    if (first) {
661                        first = false;
662                    }
663                    else {
664                        buffer.append(", ");
665                    }
666                    Map.Entry entry = (Map.Entry) iter.next();
667                    buffer.append(format(entry.getKey(), verbose));
668                    buffer.append(":");
669                    buffer.append(format(entry.getValue(), verbose));
670                }
671                buffer.append("]");
672                return buffer.toString();
673            }
674            else if (arguments instanceof Element) {
675                Element node = (Element) arguments;
676                OutputFormat format = new OutputFormat(node.getOwnerDocument());
677                format.setOmitXMLDeclaration(true);
678                format.setIndenting(true);
679                format.setLineWidth(0);
680                format.setPreserveSpace(true);
681                StringWriter sw = new StringWriter();
682                XMLSerializer serializer = new XMLSerializer(sw, format);
683                try {
684                    serializer.asDOMSerializer();
685                    serializer.serialize(node);
686                }
687                catch (IOException e) {
688                }
689                return sw.toString();
690            }
691            else if (arguments instanceof String) {
692                if (verbose) {
693                    String arg = ((String)arguments).replaceAll("\\n", "\\\\n");    // line feed
694                    arg = arg.replaceAll("\\r", "\\\\r");      // carriage return
695                    arg = arg.replaceAll("\\t", "\\\\t");      // tab
696                    arg = arg.replaceAll("\\f", "\\\\f");      // form feed
697                    arg = arg.replaceAll("\\\"", "\\\\\"");    // double quotation amrk
698                    arg = arg.replaceAll("\\\\", "\\\\");      // back slash
699                    return "\"" + arg + "\"";
700                }
701                else {
702                    return (String) arguments;
703                }
704            }
705            else {
706                return arguments.toString();
707            }
708        }
709    
710        /**
711         * Looks up the given property of the given object
712         */
713        public Object getProperty(Object object, String property) {
714            if (object == null) {
715                throw new NullPointerException("Cannot get property: " + property + " on null object");
716            }
717            else if (object instanceof GroovyObject) {
718                GroovyObject pogo = (GroovyObject) object;
719                return pogo.getProperty(property);
720            }
721            else if (object instanceof Map) {
722                Map map = (Map) object;
723                return map.get(property);
724            }
725            else {
726                return metaRegistry.getMetaClass(object.getClass()).getProperty(object, property);
727            }
728        }
729    
730        /**
731         * Sets the property on the given object
732         */
733        public void setProperty(Object object, String property, Object newValue) {
734            if (object == null) {
735                throw new GroovyRuntimeException("Cannot set property on null object");
736            }
737            else if (object instanceof GroovyObject) {
738                GroovyObject pogo = (GroovyObject) object;
739                pogo.setProperty(property, newValue);
740            }
741            else if (object instanceof Map) {
742                Map map = (Map) object;
743                map.put(property, newValue);
744            }
745            else {
746                metaRegistry.getMetaClass(object.getClass()).setProperty(object, property, newValue);
747            }
748        }
749    
750        /**
751         * Looks up the given attribute (field) on the given object
752         */
753        public Object getAttribute(Object object, String attribute) {
754            if (object == null) {
755                throw new NullPointerException("Cannot get attribute: " + attribute + " on null object");
756    
757                /**
758                 } else if (object instanceof GroovyObject) {
759                 GroovyObject pogo = (GroovyObject) object;
760                 return pogo.getAttribute(attribute);
761                 } else if (object instanceof Map) {
762                 Map map = (Map) object;
763                 return map.get(attribute);
764                 */
765            }
766            else {
767                return metaRegistry.getMetaClass(object.getClass()).getAttribute(object, attribute);
768            }
769        }
770    
771        /**
772         * Sets the given attribute (field) on the given object
773         */
774        public void setAttribute(Object object, String attribute, Object newValue) {
775            if (object == null) {
776                throw new GroovyRuntimeException("Cannot set attribute on null object");
777                /*
778            } else if (object instanceof GroovyObject) {
779                GroovyObject pogo = (GroovyObject) object;
780                pogo.setProperty(attribute, newValue);
781            } else if (object instanceof Map) {
782                Map map = (Map) object;
783                map.put(attribute, newValue);
784                */
785            }
786            else {
787                metaRegistry.getMetaClass(object.getClass()).setAttribute(object, attribute, newValue);
788            }
789        }
790    
791        /**
792         * Returns the method pointer for the given object name
793         */
794        public Closure getMethodPointer(Object object, String methodName) {
795            if (object == null) {
796                throw new NullPointerException("Cannot access method pointer for '" + methodName + "' on null object");
797            }
798            return metaRegistry.getMetaClass(object.getClass()).getMethodPointer(object, methodName);
799        }
800    
801    
802        /**
803         * Attempts to load the given class via name using the current class loader
804         * for this code or the thread context class loader
805         */
806        protected Class loadClass(String type) {
807            try {
808                return getClass().getClassLoader().loadClass(type);
809            }
810            catch (ClassNotFoundException e) {
811                try {
812                    return Thread.currentThread().getContextClassLoader().loadClass(type);
813                }
814                catch (ClassNotFoundException e2) {
815                    try {
816                        return Class.forName(type);
817                    }
818                    catch (ClassNotFoundException e3) {
819                    }
820                }
821                throw new GroovyRuntimeException("Could not load type: " + type, e);
822            }
823        }
824    
825        /**
826         * Find the right hand regex within the left hand string and return a matcher.
827         *
828         * @param left  string to compare
829         * @param right regular expression to compare the string to
830         * @return
831         */
832        public Matcher objectFindRegex(Object left, Object right) {
833            String stringToCompare;
834            if (left instanceof String) {
835                stringToCompare = (String) left;
836            }
837            else {
838                stringToCompare = toString(left);
839            }
840            String regexToCompareTo;
841            if (right instanceof String) {
842                regexToCompareTo = (String) right;
843            }
844            else if (right instanceof Pattern) {
845                Pattern pattern = (Pattern) right;
846                return pattern.matcher(stringToCompare);
847            }
848            else {
849                regexToCompareTo = toString(right);
850            }
851            Matcher matcher = Pattern.compile(regexToCompareTo).matcher(stringToCompare);
852            return matcher;
853        }
854    
855        /**
856         * Find the right hand regex within the left hand string and return a matcher.
857         *
858         * @param left  string to compare
859         * @param right regular expression to compare the string to
860         * @return
861         */
862        public boolean objectMatchRegex(Object left, Object right) {
863            Pattern pattern;
864            if (right instanceof Pattern) {
865                pattern = (Pattern) right;
866            }
867            else {
868                pattern = Pattern.compile(toString(right));
869            }
870            String stringToCompare = toString(left);
871            Matcher matcher = pattern.matcher(stringToCompare);
872            RegexSupport.setLastMatcher(matcher);
873            return matcher.matches();
874        }
875    
876        /**
877         * Compile a regular expression from a string.
878         *
879         * @param regex
880         * @return
881         */
882        public Pattern regexPattern(Object regex) {
883            return Pattern.compile(regex.toString());
884        }
885    
886        public Object asType(Object object, Class type) {
887            if (object == null) {
888                return null;
889            }
890            // TODO we should move these methods to groovy method, like g$asType() so that
891            // we can use operator overloading to customize on a per-type basis
892            if (type.isArray()) {
893                return asArray(object, type);
894    
895            }
896            if (type.isInstance(object)) {
897                return object;
898            }
899            if (type.isAssignableFrom(Collection.class)) {
900                if (object.getClass().isArray()) {
901                    // lets call the collections constructor
902                    // passing in the list wrapper
903                    Collection answer = null;
904                    try {
905                        answer = (Collection) type.newInstance();
906                    }
907                    catch (Exception e) {
908                        throw new ClassCastException("Could not instantiate instance of: " + type.getName() + ". Reason: " + e);
909                    }
910    
911                    // we cannot just wrap in a List as we support primitive type arrays
912                    int length = Array.getLength(object);
913                    for (int i = 0; i < length; i++) {
914                        Object element = Array.get(object, i);
915                        answer.add(element);
916                    }
917                    return answer;
918                }
919            }
920            if (type.equals(String.class)) {
921                return object.toString();
922            }
923            if (type.equals(Character.class)) {
924                if (object instanceof Number) {
925                    return asCharacter((Number) object);
926                }
927                else {
928                    String text = object.toString();
929                    if (text.length() == 1) {
930                        return new Character(text.charAt(0));
931                    }
932                    else {
933                        throw new ClassCastException("Cannot cast: " + text + " to a Character");
934                    }
935                }
936            }
937            if (Number.class.isAssignableFrom(type)) {
938                if (object instanceof Character) {
939                    return new Integer(((Character) object).charValue());
940                }
941                else if (object instanceof String) {
942                    String c = (String) object;
943                    if (c.length() == 1) {
944                        return new Integer(c.charAt(0));
945                    }
946                    else {
947                        throw new ClassCastException("Cannot cast: '" + c + "' to an Integer");
948                    }
949                }
950            }
951            if (object instanceof Number) {
952                Number n = (Number) object;
953                if (type.isPrimitive()) {
954                    if (type == byte.class) {
955                        return new Byte(n.byteValue());
956                    }
957                    if (type == char.class) {
958                        return new Character((char) n.intValue());
959                    }
960                    if (type == short.class) {
961                        return new Short(n.shortValue());
962                    }
963                    if (type == int.class) {
964                        return new Integer(n.intValue());
965                    }
966                    if (type == long.class) {
967                        return new Long(n.longValue());
968                    }
969                    if (type == float.class) {
970                        return new Float(n.floatValue());
971                    }
972                    if (type == double.class) {
973                        Double answer = new Double(n.doubleValue());
974                        //throw a runtime exception if conversion would be out-of-range for the type.
975                        if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY
976                                || answer.doubleValue() == Double.POSITIVE_INFINITY)) {
977                            throw new GroovyRuntimeException("Automatic coercion of " + n.getClass().getName()
978                                    + " value " + n + " to double failed.  Value is out of range.");
979                        }
980                        return answer;
981                    }
982                }
983                else {
984                    if (Number.class.isAssignableFrom(type)) {
985                        if (type == Byte.class) {
986                            return new Byte(n.byteValue());
987                        }
988                        if (type == Character.class) {
989                            return new Character((char) n.intValue());
990                        }
991                        if (type == Short.class) {
992                            return new Short(n.shortValue());
993                        }
994                        if (type == Integer.class) {
995                            return new Integer(n.intValue());
996                        }
997                        if (type == Long.class) {
998                            return new Long(n.longValue());
999                        }
1000                        if (type == Float.class) {
1001                            return new Float(n.floatValue());
1002                        }
1003                        if (type == Double.class) {
1004                            Double answer = new Double(n.doubleValue());
1005                            //throw a runtime exception if conversion would be out-of-range for the type.
1006                            if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY
1007                                    || answer.doubleValue() == Double.POSITIVE_INFINITY)) {
1008                                throw new GroovyRuntimeException("Automatic coercion of " + n.getClass().getName()
1009                                        + " value " + n + " to double failed.  Value is out of range.");
1010                            }
1011                            return answer;
1012                        }
1013    
1014                    }
1015                }
1016            }
1017            if (type == Boolean.class) {
1018                return asBool(object) ? Boolean.TRUE : Boolean.FALSE;
1019            }
1020            Object[] args = null;
1021            if (object instanceof Collection) {
1022                Collection list = (Collection) object;
1023                args = list.toArray();
1024            }
1025            else if (object instanceof Object[]) {
1026                args = (Object[]) object;
1027            }
1028            if (args != null) {
1029                // lets try invoke the constructor with the list as arguments
1030                // such as for creating a Dimension, Point, Color etc.
1031                try {
1032                    return invokeConstructorOf(type, args);
1033                }
1034                catch (Exception e) {
1035                    // lets ignore exception and return the original object
1036                    // as the caller has more context to be able to throw a more
1037                    // meaningful exception
1038                }
1039    
1040            }
1041            return object;
1042        }
1043    
1044        public Object asArray(Object object, Class type) {
1045            Collection list = asCollection(object);
1046            int size = list.size();
1047            Class elementType = type.getComponentType();
1048            Object array = Array.newInstance(elementType, size);
1049            int idx = 0;
1050    
1051            if (boolean.class.equals(elementType)) {
1052                for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
1053                    Object element = iter.next();
1054                    Array.setBoolean(array, idx, asBool(element));
1055                }
1056            }
1057            else if (byte.class.equals(elementType)) {
1058                for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
1059                    Object element = iter.next();
1060                    Array.setByte(array, idx, asByte(element));
1061                }
1062            }
1063            else if (char.class.equals(elementType)) {
1064                for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
1065                    Object element = iter.next();
1066                    Array.setChar(array, idx, asChar(element));
1067                }
1068            }
1069            else if (double.class.equals(elementType)) {
1070                for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
1071                    Object element = iter.next();
1072                    Array.setDouble(array, idx, asDouble(element));
1073                }
1074            }
1075            else if (float.class.equals(elementType)) {
1076                for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
1077                    Object element = iter.next();
1078                    Array.setFloat(array, idx, asFloat(element));
1079                }
1080            }
1081            else if (int.class.equals(elementType)) {
1082                for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
1083                    Object element = iter.next();
1084                    Array.setInt(array, idx, asInt(element));
1085                }
1086            }
1087            else if (long.class.equals(elementType)) {
1088                for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
1089                    Object element = iter.next();
1090                    Array.setLong(array, idx, asLong(element));
1091                }
1092            }
1093            else if (short.class.equals(elementType)) {
1094                for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
1095                    Object element = iter.next();
1096                    Array.setShort(array, idx, asShort(element));
1097                }
1098            }
1099            else {
1100                for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
1101                    Object element = iter.next();
1102                    Object coercedElement = asType(element, elementType);
1103                    Array.set(array, idx, coercedElement);
1104                }
1105            }
1106            return array;
1107        }
1108    
1109        public Number asNumber(Object value) {
1110            if (value instanceof Number) {
1111                return (Number) value;
1112            }
1113            else if (value instanceof String) {
1114                String s = (String) value;
1115    
1116                if (s.length() == 1) {
1117                    return new Integer(s.charAt(0));
1118                }
1119                else {
1120                    return new BigDecimal(s);
1121                }
1122            }
1123            else if (value instanceof Character) {
1124                return new Integer(((Character) value).charValue());
1125            }
1126            else {
1127                throw new GroovyRuntimeException("Could not convert object: " + value + " into a Number");
1128            }
1129        }
1130    
1131        public byte asByte(Object element) {
1132            return asNumber(element).byteValue();
1133        }
1134    
1135        public char asChar(Object element) {
1136            if (element instanceof String) {
1137                return asCharacter((String) element).charValue();
1138            }
1139            return asCharacter(asNumber(element)).charValue();
1140        }
1141    
1142        public float asFloat(Object element) {
1143            return asNumber(element).floatValue();
1144        }
1145    
1146        public double asDouble(Object element) {
1147            return asNumber(element).doubleValue();
1148        }
1149    
1150        public short asShort(Object element) {
1151            return asNumber(element).shortValue();
1152        }
1153    
1154        public int asInt(Object element) {
1155            return asNumber(element).intValue();
1156        }
1157    
1158        public long asLong(Object element) {
1159            return asNumber(element).longValue();
1160        }
1161    
1162        public boolean asBool(Object object) {
1163            /*
1164            if (object instanceof Boolean) {
1165                Boolean booleanValue = (Boolean) object;
1166                return booleanValue.booleanValue();
1167            }
1168            else if (object instanceof Matcher) {
1169                Matcher matcher = (Matcher) object;
1170                RegexSupport.setLastMatcher(matcher);
1171                return matcher.find();
1172            }
1173            else if (object instanceof Collection) {
1174                Collection collection = (Collection) object;
1175                return !collection.isEmpty();
1176            }
1177            else if (object instanceof String) {
1178                String string = (String) object;
1179                return string.length() > 0;
1180            }
1181            else if (object instanceof Number) {
1182                Number n = (Number) object;
1183                return n.doubleValue() != 0;
1184            }
1185            else {
1186                return object != null;
1187            }
1188            */
1189            return AsmClassGenerator.asBool(object);
1190        }
1191    
1192        protected Character asCharacter(Number value) {
1193            return new Character((char) value.intValue());
1194        }
1195    
1196        protected Character asCharacter(String text) {
1197            return new Character(text.charAt(0));
1198        }
1199    
1200        /**
1201         * @return true if the given value is a valid character string (i.e. has length of 1)
1202         */
1203        protected boolean isValidCharacterString(Object value) {
1204            if (value instanceof String) {
1205                String s = (String) value;
1206                if (s.length() == 1) {
1207                    return true;
1208                }
1209            }
1210            return false;
1211        }
1212    
1213        public void removeMetaClass(Class clazz) {
1214            getMetaRegistry().removeMetaClass(clazz);
1215        }
1216    }