blob: 82714872f7393141f73f54a4c1d0e36d3b504c4e [file] [log] [blame]
/*
* Copyright 2003-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.runtime;
import groovy.lang.*;
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
import org.codehaus.groovy.runtime.typehandling.IntegerCache;
import org.w3c.dom.Element;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
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.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 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 = 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 = 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;
}
if (value instanceof List) {
return (List) value;
}
if (value.getClass().isArray()) {
return Arrays.asList((Object[]) value);
}
if (value instanceof Enumeration) {
List answer = new ArrayList();
for (Enumeration e = (Enumeration) value; e.hasMoreElements();) {
answer.add(e.nextElement());
}
return answer;
}
// 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);
if (arguments instanceof Collection)
return toListString((Collection) arguments);
if (arguments instanceof Map)
return toMapString((Map) arguments);
if (arguments instanceof Collection)
return format(arguments, true);
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());
}
if (value instanceof Long) {
Long number = (Long) value;
return new Long(-number.longValue());
}
if (value instanceof BigInteger) {
return ((BigInteger) value).negate();
}
if (value instanceof BigDecimal) {
return ((BigDecimal) value).negate();
}
if (value instanceof Double) {
Double number = (Double) value;
return new Double(-number.doubleValue());
}
if (value instanceof Float) {
Float number = (Float) value;
return new Float(-number.floatValue());
}
if (value instanceof ArrayList) {
// value is an list.
List newlist = new ArrayList();
Iterator it = ((ArrayList) value).iterator();
for (; it.hasNext();) {
newlist.add(negate(it.next()));
}
return newlist;
}
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
*/
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
*/
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);
}
throw new SpreadMapEvaluatingException("Cannot spread the map " + value.getClass().getName() + ", value " + value);
}
public static List createList(Object[] values) {
List answer = new ArrayList(values.length);
answer.addAll(Arrays.asList(values));
return answer;
}
public static Map createMap(Object[] values) {
Map answer = new LinkedHashMap(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 = 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);
}
throw new AssertionError(String.valueOf(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
*/
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) {
// Ignore
}
}
}
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) {
final NullObject nullObject = NullObject.getNullObject();
return (String) nullObject.getMetaClass().invokeMethod(nullObject, "toString", EMPTY_ARGS);
}
if (arguments.getClass().isArray()) {
return format(DefaultTypeTransformation.asCollection(arguments), verbose);
}
if (arguments instanceof Range) {
Range range = (Range) arguments;
if (verbose) {
return range.inspect();
} else {
return range.toString();
}
}
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();
}
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();
}
if (arguments instanceof Element) {
Element node = (Element) arguments;
TransformerFactory transformerFactory = TransformerFactory.newInstance();
StringWriter sw = new StringWriter();
try {
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty("indent", "yes");
transformer.transform(new DOMSource(node), new StreamResult(sw));
}
catch (TransformerException e) {
// Ignore
}
return sw.toString();
}
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;
}
}
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());
}
if (value instanceof Long) {
Long number = (Long) value;
return new Long(~number.longValue());
}
if (value instanceof BigInteger) {
return ((BigInteger) value).not();
}
if (value instanceof String) {
// value is a regular expression.
return DefaultGroovyMethods.negate(value.toString());
}
if (value instanceof GString) {
// value is a regular expression.
return DefaultGroovyMethods.negate(value.toString());
}
if (value instanceof ArrayList) {
// value is an list.
List newlist = new ArrayList();
Iterator it = ((ArrayList) value).iterator();
for (; it.hasNext();) {
newlist.add(bitNegate(it.next()));
}
return newlist;
}
throw new BitwiseNegateEvaluatingException("Cannot bitwise negate type " + value.getClass().getName() + ", value " + value);
}
}