View Javadoc

1   /*
2    $Id: InvokerHelper.java 4294 2006-12-02 19:31:27Z blackdrag $
3   
4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5   
6    Redistribution and use of this software and associated documentation
7    ("Software"), with or without modification, are permitted provided
8    that the following conditions are met:
9   
10   1. Redistributions of source code must retain copyright
11      statements and notices.  Redistributions must also contain a
12      copy of this document.
13  
14   2. Redistributions in binary form must reproduce the
15      above copyright notice, this list of conditions and the
16      following disclaimer in the documentation and/or other
17      materials provided with the distribution.
18  
19   3. The name "groovy" must not be used to endorse or promote
20      products derived from this Software without prior written
21      permission of The Codehaus.  For written permission,
22      please contact info@codehaus.org.
23  
24   4. Products derived from this Software may not be called "groovy"
25      nor may "groovy" appear in their names without prior written
26      permission of The Codehaus. "groovy" is a registered
27      trademark of The Codehaus.
28  
29   5. Due credit should be given to The Codehaus -
30      http://groovy.codehaus.org/
31  
32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
43   OF THE POSSIBILITY OF SUCH DAMAGE.
44  
45   */
46  package org.codehaus.groovy.runtime;
47  
48  import groovy.lang.*;
49  
50  import java.beans.Introspector;
51  import java.io.IOException;
52  import java.io.InputStream;
53  import java.io.InputStreamReader;
54  import java.io.Reader;
55  import java.io.StringWriter;
56  import java.io.Writer;
57  import java.math.BigDecimal;
58  import java.math.BigInteger;
59  import java.util.ArrayList;
60  import java.util.Arrays;
61  import java.util.Collection;
62  import java.util.Collections;
63  import java.util.Enumeration;
64  import java.util.HashMap;
65  import java.util.Iterator;
66  import java.util.List;
67  import java.util.Map;
68  import java.util.regex.Matcher;
69  import java.util.regex.Pattern;
70  
71  import org.apache.xml.serialize.OutputFormat;
72  import org.apache.xml.serialize.XMLSerializer;
73  import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
74  import org.codehaus.groovy.runtime.typehandling.IntegerCache;
75  import org.w3c.dom.Element;
76  
77  /***
78   * A static helper class to make bytecode generation easier and act as a facade over the Invoker
79   *
80   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
81   * @version $Revision: 4294 $
82   */
83  public class InvokerHelper {
84      public static final Object[] EMPTY_ARGS = {
85      };
86  
87      private static final Object[] EMPTY_MAIN_ARGS = new Object[]{new String[0]};
88  
89      private static final Invoker singleton = new Invoker();
90  
91  
92  
93      public static MetaClass getMetaClass(Object object) {
94          return getInstance().getMetaClass(object);
95      }
96  
97      public static void removeClass(Class clazz) {
98          getInstance().removeMetaClass(clazz);
99          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 }