001    /*
002     $Id: InvokerHelper.java 4294 2006-12-02 19:31:27Z blackdrag $
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 groovy.lang.*;
049    
050    import java.beans.Introspector;
051    import java.io.IOException;
052    import java.io.InputStream;
053    import java.io.InputStreamReader;
054    import java.io.Reader;
055    import java.io.StringWriter;
056    import java.io.Writer;
057    import java.math.BigDecimal;
058    import java.math.BigInteger;
059    import java.util.ArrayList;
060    import java.util.Arrays;
061    import java.util.Collection;
062    import java.util.Collections;
063    import java.util.Enumeration;
064    import java.util.HashMap;
065    import java.util.Iterator;
066    import java.util.List;
067    import java.util.Map;
068    import java.util.regex.Matcher;
069    import java.util.regex.Pattern;
070    
071    import org.apache.xml.serialize.OutputFormat;
072    import org.apache.xml.serialize.XMLSerializer;
073    import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
074    import org.codehaus.groovy.runtime.typehandling.IntegerCache;
075    import org.w3c.dom.Element;
076    
077    /**
078     * A static helper class to make bytecode generation easier and act as a facade over the Invoker
079     *
080     * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
081     * @version $Revision: 4294 $
082     */
083    public class InvokerHelper {
084        public static final Object[] EMPTY_ARGS = {
085        };
086    
087        private static final Object[] EMPTY_MAIN_ARGS = new Object[]{new String[0]};
088    
089        private static final Invoker singleton = new Invoker();
090    
091    
092    
093        public static MetaClass getMetaClass(Object object) {
094            return getInstance().getMetaClass(object);
095        }
096    
097        public static void removeClass(Class clazz) {
098            getInstance().removeMetaClass(clazz);
099            Introspector.flushFromCaches(clazz);
100        }
101    
102        public static Invoker getInstance() {
103            return singleton;
104        }
105    
106        public static Object invokeNoArgumentsMethod(Object object, String methodName) {
107            return getInstance().invokeMethod(object, methodName, EMPTY_ARGS);
108        }
109    
110        public static Object invokeMethod(Object object, String methodName, Object arguments) {
111            return getInstance().invokeMethod(object, methodName, arguments);
112        }
113    
114        public static Object invokeSuperMethod(Object object, String methodName, Object arguments) {
115            return getInstance().invokeSuperMethod(object, methodName, arguments);
116        }
117    
118        public static Object invokeMethodSafe(Object object, String methodName, Object arguments) {
119            if (object != null) {
120                return getInstance().invokeMethod(object, methodName, arguments);
121            }
122            return null;
123        }
124    
125        public static Object invokeStaticMethod(Class type, String methodName, Object arguments) {
126            return getInstance().invokeStaticMethod(type, methodName, arguments);
127        }
128        
129        public static Object invokeStaticMethod(String klass, String methodName, Object arguments) throws ClassNotFoundException {
130            Class type = InvokerHelper.class.forName(klass);
131            return getInstance().invokeStaticMethod(type, methodName, arguments);
132        }
133        
134    
135        public static Object invokeStaticNoArgumentsMethod(Class type, String methodName) {
136            return getInstance().invokeStaticMethod(type, methodName, EMPTY_ARGS);
137        }
138        
139        public static Object invokeConstructorOf(Class type, Object arguments) {
140            return getInstance().invokeConstructorOf(type, arguments);
141        }
142        
143        public static Object invokeConstructorOf(String klass, Object arguments) throws ClassNotFoundException {
144            Class type = InvokerHelper.class.forName(klass);
145            return getInstance().invokeConstructorOf(type, arguments);
146        }
147    
148        public static Object invokeNoArgumentsConstructorOf(Class type) {
149            return getInstance().invokeConstructorOf(type, EMPTY_ARGS);
150        }
151    
152        public static Object invokeClosure(Object closure, Object arguments) {
153            return getInstance().invokeMethod(closure, "doCall", arguments);
154        }
155    
156        public static List asList(Object value) {
157            if (value == null) {
158                return Collections.EMPTY_LIST;
159            }
160            else if (value instanceof List) {
161                return (List) value;
162            }
163            else if (value.getClass().isArray()) {
164                return Arrays.asList((Object[]) value);
165            }
166            else if (value instanceof Enumeration) {
167                List answer = new ArrayList();
168                for (Enumeration e = (Enumeration) value; e.hasMoreElements();) {
169                    answer.add(e.nextElement());
170                }
171                return answer;
172            }
173            else {
174                // lets assume its a collection of 1
175                return Collections.singletonList(value);
176            }
177        }
178    
179        public static String toString(Object arguments) {
180            if (arguments instanceof Object[])
181                return toArrayString((Object[])arguments);
182            else if (arguments instanceof Collection)
183                return toListString((Collection)arguments);
184            else if (arguments instanceof Map)
185                return toMapString((Map)arguments);
186            else if (arguments instanceof Collection)
187                return format(arguments, true);
188            else
189                return format(arguments, false);
190        }
191    
192        public static String inspect(Object self) {
193            return format(self, true);
194        }
195    
196        public static Object getAttribute(Object object, String attribute) {
197            return getInstance().getAttribute(object, attribute);
198        }
199    
200        public static void setAttribute(Object object, String attribute, Object newValue) {
201            getInstance().setAttribute(object, attribute, newValue);
202        }
203    
204        public static Object getProperty(Object object, String property) {
205            return getInstance().getProperty(object, property);
206        }
207    
208        public static Object getPropertySafe(Object object, String property) {
209            if (object != null) {
210                return getInstance().getProperty(object, property);
211            }
212            return null;
213        }
214    
215        public static void setProperty(Object object, String property, Object newValue) {
216            getInstance().setProperty(object, property, newValue);
217        }
218    
219        /**
220         * This is so we don't have to reorder the stack when we call this method.
221         * At some point a better name might be in order.
222         */
223        public static void setProperty2(Object newValue, Object object, String property) {
224            getInstance().setProperty(object, property, newValue);
225        }
226    
227    
228        /**
229         * This is so we don't have to reorder the stack when we call this method.
230         * At some point a better name might be in order.
231         */
232        public static void setGroovyObjectProperty(Object newValue, GroovyObject object, String property) {
233            object.setProperty(property, newValue);
234        }
235    
236        public static Object getGroovyObjectProperty(GroovyObject object, String property) {
237            return object.getProperty(property);
238        }
239    
240    
241        /**
242         * This is so we don't have to reorder the stack when we call this method.
243         * At some point a better name might be in order.
244         */
245        public static void setPropertySafe2(Object newValue, Object object, String property) {
246            if (object != null) {
247                setProperty2(newValue, object, property);
248            }
249        }
250    
251        /**
252         * Returns the method pointer for the given object name
253         */
254        public static Closure getMethodPointer(Object object, String methodName) {
255            return getInstance().getMethodPointer(object, methodName);
256        }
257    
258        public static Object negate(Object value) {
259            if (value instanceof Integer) {
260                Integer number = (Integer) value;
261                return IntegerCache.integerValue(-number.intValue());
262            }
263            else if (value instanceof Long) {
264                Long number = (Long) value;
265                return new Long(-number.longValue());
266            }
267            else if (value instanceof BigInteger) {
268                return ((BigInteger) value).negate();
269            }
270            else if (value instanceof BigDecimal) {
271                return ((BigDecimal) value).negate();
272            }
273            else if (value instanceof Double) {
274                Double number = (Double) value;
275                return new Double(-number.doubleValue());
276            }
277            else if (value instanceof Float) {
278                Float number = (Float) value;
279                return new Float(-number.floatValue());
280            }
281            else if (value instanceof ArrayList) {
282                // value is an list.
283                ArrayList newlist = new ArrayList();
284                Iterator it = ((ArrayList) value).iterator();
285                for (; it.hasNext();) {
286                    newlist.add(negate(it.next()));
287                }
288                return newlist;
289            }
290            else {
291                throw new GroovyRuntimeException("Cannot negate type " + value.getClass().getName() + ", value " + value);
292            }
293        }
294    
295        /**
296         * Find the right hand regex within the left hand string and return a matcher.
297         *
298         * @param left  string to compare
299         * @param right regular expression to compare the string to
300         * @return
301         */
302        public static Matcher findRegex(Object left, Object right) {
303            String stringToCompare;
304            if (left instanceof String) {
305                stringToCompare = (String) left;
306            }
307            else {
308                stringToCompare = toString(left);
309            }
310            String regexToCompareTo;
311            if (right instanceof String) {
312                regexToCompareTo = (String) right;
313            }
314            else if (right instanceof Pattern) {
315                Pattern pattern = (Pattern) right;
316                return pattern.matcher(stringToCompare);
317            }
318            else {
319                regexToCompareTo = toString(right);
320            }
321            Matcher matcher = Pattern.compile(regexToCompareTo).matcher(stringToCompare);
322            return matcher;
323        }
324        
325        
326        /**
327         * Find the right hand regex within the left hand string and return a matcher.
328         *
329         * @param left  string to compare
330         * @param right regular expression to compare the string to
331         * @return
332         */
333        public static boolean matchRegex(Object left, Object right) {
334            Pattern pattern;
335            if (right instanceof Pattern) {
336                pattern = (Pattern) right;
337            }
338            else {
339                pattern = Pattern.compile(toString(right));
340            }
341            String stringToCompare = toString(left);
342            Matcher matcher = pattern.matcher(stringToCompare);
343            RegexSupport.setLastMatcher(matcher);
344            return matcher.matches();
345        }
346    
347        public static Tuple createTuple(Object[] array) {
348            return new Tuple(array);
349        }
350    
351        public static SpreadMap spreadMap(Object value) {
352            if (value instanceof Map) {
353                Object[] values = new Object[((Map) value).keySet().size() * 2];
354                int index = 0;
355                Iterator it = ((Map) value).keySet().iterator();
356                for (; it.hasNext();) {
357                    Object key = it.next();
358                    values[index++] = key;
359                    values[index++] = ((Map) value).get(key);
360                }
361                return new SpreadMap(values);
362            }
363            else {
364                throw new SpreadMapEvaluatingException("Cannot spread the map " + value.getClass().getName() + ", value " + value);
365            }
366        }
367    
368        public static List createList(Object[] values) {
369            ArrayList answer = new ArrayList(values.length);
370            for (int i = 0; i < values.length; i++) {
371                answer.add(values[i]);
372            }
373            return answer;
374        }
375    
376        public static Map createMap(Object[] values) {
377            Map answer = new HashMap(values.length / 2);
378            int i = 0;
379            while (i < values.length - 1) {
380                if ((values[i] instanceof SpreadMap) && (values[i+1] instanceof Map)) {
381                    Map smap = (Map) values[i+1];
382                    Iterator iter = smap.keySet().iterator();
383                    for (; iter.hasNext(); ) {
384                        Object key = (Object) iter.next();
385                        answer.put(key, smap.get(key));
386                    }
387                    i+=2;
388                }
389                else {
390                    answer.put(values[i++], values[i++]);
391                }
392            }
393            return answer;
394        }
395    
396        public static void assertFailed(Object expression, Object message) {
397            if (message == null || "".equals(message)) {
398                throw new AssertionError("Expression: " + expression);
399            }
400            else {
401                throw new AssertionError("" + message + ". Expression: " + expression);
402            }
403        }
404    
405        public static Object runScript(Class scriptClass, String[] args) {
406            Binding context = new Binding(args);
407            Script script = createScript(scriptClass, context);
408            return invokeMethod(script, "run", EMPTY_ARGS);
409        }
410    
411        public static Script createScript(Class scriptClass, Binding context) {
412            // for empty scripts
413            if (scriptClass == null) {
414                return new Script() {
415                    public Object run() {
416                        return null;
417                    }
418                };
419            }
420            try {
421                final GroovyObject object = (GroovyObject) scriptClass.newInstance();
422                Script script = null;
423                if (object instanceof Script) {
424                    script = (Script) object;
425                }
426                else {
427                    // it could just be a class, so lets wrap it in a Script wrapper
428                    // though the bindings will be ignored
429                    script = new Script() {
430                        public Object run() {
431                            object.invokeMethod("main", EMPTY_MAIN_ARGS);
432                            return null;
433                        }
434                    };
435                    setProperties(object, context.getVariables());
436                }
437                script.setBinding(context);
438                return script;
439            }
440            catch (Exception e) {
441                throw new GroovyRuntimeException("Failed to create Script instance for class: " + scriptClass + ". Reason: " + e,
442                        e);
443            }
444        }
445    
446        /**
447         * Sets the properties on the given object
448         *
449         * @param object
450         * @param map
451         */
452        public static void setProperties(Object object, Map map) {
453            MetaClass mc = getInstance().getMetaClass(object);
454            for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
455                Map.Entry entry = (Map.Entry) iter.next();
456                String key = entry.getKey().toString();
457    
458                Object value = entry.getValue();
459                try {
460                    mc.setProperty(object, key, value);
461                } catch (MissingPropertyException mpe) {}
462            }
463        }
464    
465        public static String getVersion() {
466            String version = null;
467            Package p = Package.getPackage("groovy.lang");
468            if (p != null) {
469                version = p.getImplementationVersion();
470            }
471            if (version == null) {
472                version = "";
473            }
474            return version;
475        }
476    
477        /**
478         * Writes the given object to the given stream
479         */
480        public static void write(Writer out, Object object) throws IOException {
481            if (object instanceof String) {
482                out.write((String) object);
483            }
484            else if (object instanceof Object[]) {
485                out.write(toArrayString((Object[]) object));
486            }
487            else if (object instanceof Map) {
488                out.write(toMapString((Map) object));
489            }
490            else if (object instanceof Collection) {
491                out.write(toListString((Collection) object));
492            }
493            else if (object instanceof Writable) {
494                Writable writable = (Writable) object;
495                writable.writeTo(out);
496            }
497            else if (object instanceof InputStream || object instanceof Reader) {
498                // Copy stream to stream
499                Reader reader;
500                if (object instanceof InputStream) {
501                    reader = new InputStreamReader((InputStream) object);
502                }
503                else {
504                    reader = (Reader) object;
505                }
506                char[] chars = new char[8192];
507                int i;
508                while ((i = reader.read(chars)) != -1) {
509                    out.write(chars, 0, i);
510                }
511                reader.close();
512            }
513            else {
514                out.write(toString(object));
515            }
516        }
517        
518        public static Iterator asIterator(Object o) {
519            return (Iterator) invokeMethod(o,"iterator",EMPTY_ARGS);
520        }
521        
522        protected static String format(Object arguments, boolean verbose) {
523            if (arguments == null) {
524                return "null";
525            }
526            else if (arguments.getClass().isArray()) {
527                return format(DefaultTypeTransformation.asCollection(arguments), verbose);
528            }
529            else if (arguments instanceof Range) {
530                Range range = (Range) arguments;
531                if (verbose) {
532                    return range.inspect();
533                }
534                else {
535                    return range.toString();
536                }
537            }
538            else if (arguments instanceof List) {
539                List list = (List) arguments;
540                StringBuffer buffer = new StringBuffer("[");
541                boolean first = true;
542                for (Iterator iter = list.iterator(); iter.hasNext();) {
543                    if (first) {
544                        first = false;
545                    }
546                    else {
547                        buffer.append(", ");
548                    }
549                    buffer.append(format(iter.next(), verbose));
550                }
551                buffer.append("]");
552                return buffer.toString();
553            }
554            else if (arguments instanceof Map) {
555                Map map = (Map) arguments;
556                if (map.isEmpty()) {
557                    return "[:]";
558                }
559                StringBuffer buffer = new StringBuffer("[");
560                boolean first = true;
561                for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
562                    if (first) {
563                        first = false;
564                    }
565                    else {
566                        buffer.append(", ");
567                    }
568                    Map.Entry entry = (Map.Entry) iter.next();
569                    buffer.append(format(entry.getKey(), verbose));
570                    buffer.append(":");
571                    if (entry.getValue()==map) {
572                        buffer.append("this Map_");
573                    } else {
574                        buffer.append(format(entry.getValue(), verbose));
575                    }
576                }
577                buffer.append("]");
578                return buffer.toString();
579            }
580            else if (arguments instanceof Element) {
581                Element node = (Element) arguments;
582                OutputFormat format = new OutputFormat(node.getOwnerDocument());
583                format.setOmitXMLDeclaration(true);
584                format.setIndenting(true);
585                format.setLineWidth(0);
586                format.setPreserveSpace(true);
587                StringWriter sw = new StringWriter();
588                XMLSerializer serializer = new XMLSerializer(sw, format);
589                try {
590                    serializer.asDOMSerializer();
591                    serializer.serialize(node);
592                }
593                catch (IOException e) {
594                }
595                return sw.toString();
596            }
597            else if (arguments instanceof String) {
598                if (verbose) {
599                    String arg = ((String)arguments).replaceAll("\\n", "\\\\n");    // line feed
600                    arg = arg.replaceAll("\\r", "\\\\r");      // carriage return
601                    arg = arg.replaceAll("\\t", "\\\\t");      // tab
602                    arg = arg.replaceAll("\\f", "\\\\f");      // form feed
603                    arg = arg.replaceAll("\\\"", "\\\\\"");    // double quotation amrk
604                    arg = arg.replaceAll("\\\\", "\\\\");      // back slash
605                    return "\"" + arg + "\"";
606                }
607                else {
608                    return (String) arguments;
609                }
610            }
611            else {
612                return arguments.toString();
613            }
614        }
615        
616    
617        /**
618         * A helper method to format the arguments types as a comma-separated list
619         */
620        public static String toTypeString(Object[] arguments) {
621            if (arguments == null) {
622                return "null";
623            }
624            StringBuffer argBuf = new StringBuffer();
625            for (int i = 0; i < arguments.length; i++) {
626                if (i > 0) {
627                    argBuf.append(", ");
628                }
629                argBuf.append(arguments[i] != null ? arguments[i].getClass().getName() : "null");
630            }
631            return argBuf.toString();
632        }
633    
634        /**
635         * A helper method to return the string representation of a map with bracket boundaries "[" and "]".
636         */
637        public static String toMapString(Map arg) {
638            return format(arg, true);
639            /*if (arg == null) {
640                return "null";
641            }
642            if (arg.isEmpty()) {
643                return "[:]";
644            }
645            String sbdry = "[";
646            String ebdry = "]";
647            StringBuffer buffer = new StringBuffer(sbdry);
648            boolean first = true;
649            for (Iterator iter = arg.entrySet().iterator(); iter.hasNext();) {
650                if (first)
651                    first = false;
652                else
653                    buffer.append(", ");
654                Map.Entry entry = (Map.Entry) iter.next();
655                buffer.append(format(entry.getKey(), true));
656                buffer.append(":");
657                buffer.append(format(entry.getValue(), true));
658            }
659            buffer.append(ebdry);
660            return buffer.toString();*/
661        }
662    
663        /**
664         * A helper method to return the string representation of a list with bracket boundaries "[" and "]".
665         */
666        public static String toListString(Collection arg) {
667            if (arg == null) {
668                return "null";
669            }
670            if (arg.isEmpty()) {
671                return "[]";
672            }
673            String sbdry = "[";
674            String ebdry = "]";
675            StringBuffer buffer = new StringBuffer(sbdry);
676            boolean first = true;
677            for (Iterator iter = arg.iterator(); iter.hasNext();) {
678                if (first)
679                    first = false;
680                else
681                    buffer.append(", ");
682                Object elem = iter.next();
683                buffer.append(format(elem, true));
684            }
685            buffer.append(ebdry);
686            return buffer.toString();
687        }
688    
689        /**
690         * A helper method to return the string representation of an arrray of objects
691         * with brace boundaries "{" and "}".
692         */
693        public static String toArrayString(Object[] arguments) {
694            if (arguments == null) {
695                return "null";
696            }
697            String sbdry = "{";
698            String ebdry = "}";
699            StringBuffer argBuf = new StringBuffer(sbdry);
700            for (int i = 0; i < arguments.length; i++) {
701                if (i > 0) {
702                    argBuf.append(", ");
703                }
704                argBuf.append(format(arguments[i], true));
705            }
706            argBuf.append(ebdry);
707            return argBuf.toString();
708        }
709        
710        public static List createRange(Object from, Object to, boolean inclusive) {
711            try {
712                return ScriptBytecodeAdapter.createRange(from,to,inclusive);
713            } catch (RuntimeException re) {
714                throw re;
715            } catch (Error e) {
716                throw e;
717            } catch (Throwable t) {
718                throw new RuntimeException(t);
719            }
720        }
721        
722        public static Object bitNegate(Object value) {
723            if (value instanceof Integer) {
724                Integer number = (Integer) value;
725                return new Integer(~number.intValue());
726            }
727            else if (value instanceof Long) {
728                Long number = (Long) value;
729                return new Long(~number.longValue());
730            }
731            else if (value instanceof BigInteger) {
732                return ((BigInteger) value).not();
733    
734            }
735            else if (value instanceof String) {
736                // value is a regular expression.
737                return DefaultGroovyMethods.negate(value.toString());
738            }
739            else if (value instanceof GString) {
740                // value is a regular expression.
741                return DefaultGroovyMethods.negate(value.toString());
742            }
743            else if (value instanceof ArrayList) {
744                // value is an list.
745                ArrayList newlist = new ArrayList();
746                Iterator it = ((ArrayList) value).iterator();
747                for (; it.hasNext();) {
748                    newlist.add(bitNegate(it.next()));
749                }
750                return newlist;
751            }
752            else {
753                throw new BitwiseNegateEvaluatingException("Cannot bitwise negate type " + value.getClass().getName() + ", value " + value);
754            }
755    
756    
757        }
758    
759    }