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