blob: 4aa4d3342ebeb0b9bcd4704b15a6af8f58c31039 [file] [log] [blame]
/*
$Id$
Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
Redistribution and use of this software and associated documentation
("Software"), with or without modification, are permitted provided
that the following conditions are met:
1. Redistributions of source code must retain copyright
statements and notices. Redistributions must also contain a
copy of this document.
2. Redistributions in binary form must reproduce the
above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
3. The name "groovy" must not be used to endorse or promote
products derived from this Software without prior written
permission of The Codehaus. For written permission,
please contact info@codehaus.org.
4. Products derived from this Software may not be called "groovy"
nor may "groovy" appear in their names without prior written
permission of The Codehaus. "groovy" is a registered
trademark of The Codehaus.
5. Due credit should be given to The Codehaus -
http://groovy.codehaus.org/
THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.codehaus.groovy.runtime;
import groovy.lang.*;
import java.beans.Introspector;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
import org.codehaus.groovy.runtime.typehandling.IntegerCache;
import org.w3c.dom.Element;
/**
* A static helper class to make bytecode generation easier and act as a facade over the Invoker
*
* @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
* @version $Revision$
*/
public class InvokerHelper {
public static final Object[] EMPTY_ARGS = {
};
private static final Object[] EMPTY_MAIN_ARGS = new Object[]{new String[0]};
private static final Invoker singleton = new Invoker();
public static MetaClass getMetaClass(Object object) {
return getInstance().getMetaClass(object);
}
public static void removeClass(Class clazz) {
getInstance().removeMetaClass(clazz);
Introspector.flushFromCaches(clazz);
}
public static Invoker getInstance() {
return singleton;
}
public static Object invokeNoArgumentsMethod(Object object, String methodName) {
return getInstance().invokeMethod(object, methodName, EMPTY_ARGS);
}
public static Object invokeMethod(Object object, String methodName, Object arguments) {
return getInstance().invokeMethod(object, methodName, arguments);
}
public static Object invokeSuperMethod(Object object, String methodName, Object arguments) {
return getInstance().invokeSuperMethod(object, methodName, arguments);
}
public static Object invokeMethodSafe(Object object, String methodName, Object arguments) {
if (object != null) {
return getInstance().invokeMethod(object, methodName, arguments);
}
return null;
}
public static Object invokeStaticMethod(Class type, String methodName, Object arguments) {
return getInstance().invokeStaticMethod(type, methodName, arguments);
}
public static Object invokeStaticMethod(String klass, String methodName, Object arguments) throws ClassNotFoundException {
Class type = InvokerHelper.class.forName(klass);
return getInstance().invokeStaticMethod(type, methodName, arguments);
}
public static Object invokeStaticNoArgumentsMethod(Class type, String methodName) {
return getInstance().invokeStaticMethod(type, methodName, EMPTY_ARGS);
}
public static Object invokeConstructorOf(Class type, Object arguments) {
return getInstance().invokeConstructorOf(type, arguments);
}
public static Object invokeConstructorOf(String klass, Object arguments) throws ClassNotFoundException {
Class type = InvokerHelper.class.forName(klass);
return getInstance().invokeConstructorOf(type, arguments);
}
public static Object invokeNoArgumentsConstructorOf(Class type) {
return getInstance().invokeConstructorOf(type, EMPTY_ARGS);
}
public static Object invokeClosure(Object closure, Object arguments) {
return getInstance().invokeMethod(closure, "doCall", arguments);
}
public static List asList(Object value) {
if (value == null) {
return Collections.EMPTY_LIST;
}
else if (value instanceof List) {
return (List) value;
}
else if (value.getClass().isArray()) {
return Arrays.asList((Object[]) value);
}
else if (value instanceof Enumeration) {
List answer = new ArrayList();
for (Enumeration e = (Enumeration) value; e.hasMoreElements();) {
answer.add(e.nextElement());
}
return answer;
}
else {
// lets assume its a collection of 1
return Collections.singletonList(value);
}
}
public static String toString(Object arguments) {
if (arguments instanceof Object[])
return toArrayString((Object[])arguments);
else if (arguments instanceof Collection)
return toListString((Collection)arguments);
else if (arguments instanceof Map)
return toMapString((Map)arguments);
else if (arguments instanceof Collection)
return format(arguments, true);
else
return format(arguments, false);
}
public static String inspect(Object self) {
return format(self, true);
}
public static Object getAttribute(Object object, String attribute) {
return getInstance().getAttribute(object, attribute);
}
public static void setAttribute(Object object, String attribute, Object newValue) {
getInstance().setAttribute(object, attribute, newValue);
}
public static Object getProperty(Object object, String property) {
return getInstance().getProperty(object, property);
}
public static Object getPropertySafe(Object object, String property) {
if (object != null) {
return getInstance().getProperty(object, property);
}
return null;
}
public static void setProperty(Object object, String property, Object newValue) {
getInstance().setProperty(object, property, newValue);
}
/**
* This is so we don't have to reorder the stack when we call this method.
* At some point a better name might be in order.
*/
public static void setProperty2(Object newValue, Object object, String property) {
getInstance().setProperty(object, property, newValue);
}
/**
* This is so we don't have to reorder the stack when we call this method.
* At some point a better name might be in order.
*/
public static void setGroovyObjectProperty(Object newValue, GroovyObject object, String property) {
object.setProperty(property, newValue);
}
public static Object getGroovyObjectProperty(GroovyObject object, String property) {
return object.getProperty(property);
}
/**
* This is so we don't have to reorder the stack when we call this method.
* At some point a better name might be in order.
*/
public static void setPropertySafe2(Object newValue, Object object, String property) {
if (object != null) {
setProperty2(newValue, object, property);
}
}
/**
* Returns the method pointer for the given object name
*/
public static Closure getMethodPointer(Object object, String methodName) {
return getInstance().getMethodPointer(object, methodName);
}
public static Object negate(Object value) {
if (value instanceof Integer) {
Integer number = (Integer) value;
return IntegerCache.integerValue(-number.intValue());
}
else if (value instanceof Long) {
Long number = (Long) value;
return new Long(-number.longValue());
}
else if (value instanceof BigInteger) {
return ((BigInteger) value).negate();
}
else if (value instanceof BigDecimal) {
return ((BigDecimal) value).negate();
}
else if (value instanceof Double) {
Double number = (Double) value;
return new Double(-number.doubleValue());
}
else if (value instanceof Float) {
Float number = (Float) value;
return new Float(-number.floatValue());
}
else if (value instanceof ArrayList) {
// value is an list.
ArrayList newlist = new ArrayList();
Iterator it = ((ArrayList) value).iterator();
for (; it.hasNext();) {
newlist.add(negate(it.next()));
}
return newlist;
}
else {
throw new GroovyRuntimeException("Cannot negate type " + value.getClass().getName() + ", value " + value);
}
}
/**
* Find the right hand regex within the left hand string and return a matcher.
*
* @param left string to compare
* @param right regular expression to compare the string to
* @return
*/
public static Matcher findRegex(Object left, Object right) {
String stringToCompare;
if (left instanceof String) {
stringToCompare = (String) left;
}
else {
stringToCompare = toString(left);
}
String regexToCompareTo;
if (right instanceof String) {
regexToCompareTo = (String) right;
}
else if (right instanceof Pattern) {
Pattern pattern = (Pattern) right;
return pattern.matcher(stringToCompare);
}
else {
regexToCompareTo = toString(right);
}
Matcher matcher = Pattern.compile(regexToCompareTo).matcher(stringToCompare);
return matcher;
}
/**
* Find the right hand regex within the left hand string and return a matcher.
*
* @param left string to compare
* @param right regular expression to compare the string to
* @return
*/
public static boolean matchRegex(Object left, Object right) {
Pattern pattern;
if (right instanceof Pattern) {
pattern = (Pattern) right;
}
else {
pattern = Pattern.compile(toString(right));
}
String stringToCompare = toString(left);
Matcher matcher = pattern.matcher(stringToCompare);
RegexSupport.setLastMatcher(matcher);
return matcher.matches();
}
public static Tuple createTuple(Object[] array) {
return new Tuple(array);
}
public static SpreadMap spreadMap(Object value) {
if (value instanceof Map) {
Object[] values = new Object[((Map) value).keySet().size() * 2];
int index = 0;
Iterator it = ((Map) value).keySet().iterator();
for (; it.hasNext();) {
Object key = it.next();
values[index++] = key;
values[index++] = ((Map) value).get(key);
}
return new SpreadMap(values);
}
else {
throw new SpreadMapEvaluatingException("Cannot spread the map " + value.getClass().getName() + ", value " + value);
}
}
public static List createList(Object[] values) {
ArrayList answer = new ArrayList(values.length);
for (int i = 0; i < values.length; i++) {
answer.add(values[i]);
}
return answer;
}
public static Map createMap(Object[] values) {
Map answer = new HashMap(values.length / 2);
int i = 0;
while (i < values.length - 1) {
if ((values[i] instanceof SpreadMap) && (values[i+1] instanceof Map)) {
Map smap = (Map) values[i+1];
Iterator iter = smap.keySet().iterator();
for (; iter.hasNext(); ) {
Object key = (Object) iter.next();
answer.put(key, smap.get(key));
}
i+=2;
}
else {
answer.put(values[i++], values[i++]);
}
}
return answer;
}
public static void assertFailed(Object expression, Object message) {
if (message == null || "".equals(message)) {
throw new AssertionError("Expression: " + expression);
}
else {
throw new AssertionError("" + message + ". Expression: " + expression);
}
}
public static Object runScript(Class scriptClass, String[] args) {
Binding context = new Binding(args);
Script script = createScript(scriptClass, context);
return invokeMethod(script, "run", EMPTY_ARGS);
}
public static Script createScript(Class scriptClass, Binding context) {
// for empty scripts
if (scriptClass == null) {
return new Script() {
public Object run() {
return null;
}
};
}
try {
final GroovyObject object = (GroovyObject) scriptClass.newInstance();
Script script = null;
if (object instanceof Script) {
script = (Script) object;
}
else {
// it could just be a class, so lets wrap it in a Script wrapper
// though the bindings will be ignored
script = new Script() {
public Object run() {
object.invokeMethod("main", EMPTY_MAIN_ARGS);
return null;
}
};
setProperties(object, context.getVariables());
}
script.setBinding(context);
return script;
}
catch (Exception e) {
throw new GroovyRuntimeException("Failed to create Script instance for class: " + scriptClass + ". Reason: " + e,
e);
}
}
/**
* Sets the properties on the given object
*
* @param object
* @param map
*/
public static void setProperties(Object object, Map map) {
MetaClass mc = getInstance().getMetaClass(object);
for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
String key = entry.getKey().toString();
Object value = entry.getValue();
try {
mc.setProperty(object, key, value);
} catch (MissingPropertyException mpe) {}
}
}
public static String getVersion() {
String version = null;
Package p = Package.getPackage("groovy.lang");
if (p != null) {
version = p.getImplementationVersion();
}
if (version == null) {
version = "";
}
return version;
}
/**
* Writes the given object to the given stream
*/
public static void write(Writer out, Object object) throws IOException {
if (object instanceof String) {
out.write((String) object);
}
else if (object instanceof Object[]) {
out.write(toArrayString((Object[]) object));
}
else if (object instanceof Map) {
out.write(toMapString((Map) object));
}
else if (object instanceof Collection) {
out.write(toListString((Collection) object));
}
else if (object instanceof Writable) {
Writable writable = (Writable) object;
writable.writeTo(out);
}
else if (object instanceof InputStream || object instanceof Reader) {
// Copy stream to stream
Reader reader;
if (object instanceof InputStream) {
reader = new InputStreamReader((InputStream) object);
}
else {
reader = (Reader) object;
}
char[] chars = new char[8192];
int i;
while ((i = reader.read(chars)) != -1) {
out.write(chars, 0, i);
}
reader.close();
}
else {
out.write(toString(object));
}
}
public static Iterator asIterator(Object o) {
return (Iterator) invokeMethod(o,"iterator",EMPTY_ARGS);
}
protected static String format(Object arguments, boolean verbose) {
if (arguments == null) {
return "null";
}
else if (arguments.getClass().isArray()) {
return format(DefaultTypeTransformation.asCollection(arguments), verbose);
}
else if (arguments instanceof Range) {
Range range = (Range) arguments;
if (verbose) {
return range.inspect();
}
else {
return range.toString();
}
}
else if (arguments instanceof List) {
List list = (List) arguments;
StringBuffer buffer = new StringBuffer("[");
boolean first = true;
for (Iterator iter = list.iterator(); iter.hasNext();) {
if (first) {
first = false;
}
else {
buffer.append(", ");
}
buffer.append(format(iter.next(), verbose));
}
buffer.append("]");
return buffer.toString();
}
else if (arguments instanceof Map) {
Map map = (Map) arguments;
if (map.isEmpty()) {
return "[:]";
}
StringBuffer buffer = new StringBuffer("[");
boolean first = true;
for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
if (first) {
first = false;
}
else {
buffer.append(", ");
}
Map.Entry entry = (Map.Entry) iter.next();
buffer.append(format(entry.getKey(), verbose));
buffer.append(":");
if (entry.getValue()==map) {
buffer.append("this Map_");
} else {
buffer.append(format(entry.getValue(), verbose));
}
}
buffer.append("]");
return buffer.toString();
}
else if (arguments instanceof Element) {
Element node = (Element) arguments;
OutputFormat format = new OutputFormat(node.getOwnerDocument());
format.setOmitXMLDeclaration(true);
format.setIndenting(true);
format.setLineWidth(0);
format.setPreserveSpace(true);
StringWriter sw = new StringWriter();
XMLSerializer serializer = new XMLSerializer(sw, format);
try {
serializer.asDOMSerializer();
serializer.serialize(node);
}
catch (IOException e) {
}
return sw.toString();
}
else if (arguments instanceof String) {
if (verbose) {
String arg = ((String)arguments).replaceAll("\\n", "\\\\n"); // line feed
arg = arg.replaceAll("\\r", "\\\\r"); // carriage return
arg = arg.replaceAll("\\t", "\\\\t"); // tab
arg = arg.replaceAll("\\f", "\\\\f"); // form feed
arg = arg.replaceAll("\\\"", "\\\\\""); // double quotation amrk
arg = arg.replaceAll("\\\\", "\\\\"); // back slash
return "\"" + arg + "\"";
}
else {
return (String) arguments;
}
}
else {
return arguments.toString();
}
}
/**
* A helper method to format the arguments types as a comma-separated list
*/
public static String toTypeString(Object[] arguments) {
if (arguments == null) {
return "null";
}
StringBuffer argBuf = new StringBuffer();
for (int i = 0; i < arguments.length; i++) {
if (i > 0) {
argBuf.append(", ");
}
argBuf.append(arguments[i] != null ? arguments[i].getClass().getName() : "null");
}
return argBuf.toString();
}
/**
* A helper method to return the string representation of a map with bracket boundaries "[" and "]".
*/
public static String toMapString(Map arg) {
return format(arg, true);
/*if (arg == null) {
return "null";
}
if (arg.isEmpty()) {
return "[:]";
}
String sbdry = "[";
String ebdry = "]";
StringBuffer buffer = new StringBuffer(sbdry);
boolean first = true;
for (Iterator iter = arg.entrySet().iterator(); iter.hasNext();) {
if (first)
first = false;
else
buffer.append(", ");
Map.Entry entry = (Map.Entry) iter.next();
buffer.append(format(entry.getKey(), true));
buffer.append(":");
buffer.append(format(entry.getValue(), true));
}
buffer.append(ebdry);
return buffer.toString();*/
}
/**
* A helper method to return the string representation of a list with bracket boundaries "[" and "]".
*/
public static String toListString(Collection arg) {
if (arg == null) {
return "null";
}
if (arg.isEmpty()) {
return "[]";
}
String sbdry = "[";
String ebdry = "]";
StringBuffer buffer = new StringBuffer(sbdry);
boolean first = true;
for (Iterator iter = arg.iterator(); iter.hasNext();) {
if (first)
first = false;
else
buffer.append(", ");
Object elem = iter.next();
buffer.append(format(elem, true));
}
buffer.append(ebdry);
return buffer.toString();
}
/**
* A helper method to return the string representation of an arrray of objects
* with brace boundaries "{" and "}".
*/
public static String toArrayString(Object[] arguments) {
if (arguments == null) {
return "null";
}
String sbdry = "{";
String ebdry = "}";
StringBuffer argBuf = new StringBuffer(sbdry);
for (int i = 0; i < arguments.length; i++) {
if (i > 0) {
argBuf.append(", ");
}
argBuf.append(format(arguments[i], true));
}
argBuf.append(ebdry);
return argBuf.toString();
}
public static List createRange(Object from, Object to, boolean inclusive) {
try {
return ScriptBytecodeAdapter.createRange(from,to,inclusive);
} catch (RuntimeException re) {
throw re;
} catch (Error e) {
throw e;
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
public static Object bitNegate(Object value) {
if (value instanceof Integer) {
Integer number = (Integer) value;
return new Integer(~number.intValue());
}
else if (value instanceof Long) {
Long number = (Long) value;
return new Long(~number.longValue());
}
else if (value instanceof BigInteger) {
return ((BigInteger) value).not();
}
else if (value instanceof String) {
// value is a regular expression.
return DefaultGroovyMethods.negate(value.toString());
}
else if (value instanceof GString) {
// value is a regular expression.
return DefaultGroovyMethods.negate(value.toString());
}
else if (value instanceof ArrayList) {
// value is an list.
ArrayList newlist = new ArrayList();
Iterator it = ((ArrayList) value).iterator();
for (; it.hasNext();) {
newlist.add(bitNegate(it.next()));
}
return newlist;
}
else {
throw new BitwiseNegateEvaluatingException("Cannot bitwise negate type " + value.getClass().getName() + ", value " + value);
}
}
}