blob: 5ef7c36dc767979a3748c94d75e630a3fa9940d7 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* 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 com.android.monkeyrunner;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.ImmutableMap.Builder;
import com.android.monkeyrunner.doc.MonkeyRunnerExported;
import org.python.core.ArgParser;
import org.python.core.Py;
import org.python.core.PyDictionary;
import org.python.core.PyFloat;
import org.python.core.PyInteger;
import org.python.core.PyList;
import org.python.core.PyNone;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.core.PyTuple;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Collection of useful utilities function for interacting with the Jython interpreter.
*/
public final class JythonUtils {
private static final Logger LOG = Logger.getLogger(JythonUtils.class.getCanonicalName());
private JythonUtils() { }
/**
* Mapping of PyObject classes to the java class we want to convert them to.
*/
private static final Map<Class<? extends PyObject>, Class<?>> PYOBJECT_TO_JAVA_OBJECT_MAP;
static {
Builder<Class<? extends PyObject>, Class<?>> builder = ImmutableMap.builder();
builder.put(PyString.class, String.class);
// What python calls float, most people call double
builder.put(PyFloat.class, Double.class);
builder.put(PyInteger.class, Integer.class);
PYOBJECT_TO_JAVA_OBJECT_MAP = builder.build();
}
/**
* Utility method to be called from Jython bindings to give proper handling of keyword and
* positional arguments.
*
* @param args the PyObject arguments from the binding
* @param kws the keyword arguments from the binding
* @return an ArgParser for this binding, or null on error
*/
public static ArgParser createArgParser(PyObject[] args, String[] kws) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
// Up 2 levels in the current stack to give us the calling function
StackTraceElement element = stackTrace[2];
String methodName = element.getMethodName();
String className = element.getClassName();
Class<?> clz;
try {
clz = Class.forName(className);
} catch (ClassNotFoundException e) {
LOG.log(Level.SEVERE, "Got exception: ", e);
return null;
}
Method m;
try {
m = clz.getMethod(methodName, PyObject[].class, String[].class);
} catch (SecurityException e) {
LOG.log(Level.SEVERE, "Got exception: ", e);
return null;
} catch (NoSuchMethodException e) {
LOG.log(Level.SEVERE, "Got exception: ", e);
return null;
}
MonkeyRunnerExported annotation = m.getAnnotation(MonkeyRunnerExported.class);
return new ArgParser(methodName, args, kws,
annotation.args());
}
/**
* Get a python floating point value from an ArgParser.
*
* @param ap the ArgParser to get the value from.
* @param position the position in the parser
* @return the double value
*/
public static double getFloat(ArgParser ap, int position) {
PyObject arg = ap.getPyObject(position);
if (Py.isInstance(arg, PyFloat.TYPE)) {
return ((PyFloat) arg).asDouble();
}
if (Py.isInstance(arg, PyInteger.TYPE)) {
return ((PyInteger) arg).asDouble();
}
throw Py.TypeError("Unable to parse argument: " + position);
}
/**
* Get a list of arguments from an ArgParser.
*
* @param ap the ArgParser
* @param position the position in the parser to get the argument from
* @return a list of those items
*/
@SuppressWarnings("unchecked")
public static List<Object> getList(ArgParser ap, int position) {
PyObject arg = ap.getPyObject(position, Py.None);
if (Py.isInstance(arg, PyNone.TYPE)) {
return Collections.emptyList();
}
List<Object> ret = Lists.newArrayList();
PyList array = (PyList) arg;
for (int x = 0; x < array.__len__(); x++) {
PyObject item = array.__getitem__(x);
Class<?> javaClass = PYOBJECT_TO_JAVA_OBJECT_MAP.get(item.getClass());
if (javaClass != null) {
ret.add(item.__tojava__(javaClass));
}
}
return ret;
}
/**
* Get a dictionary from an ArgParser. For ease of use, key types are always coerced to
* strings. If key type cannot be coeraced to string, an exception is raised.
*
* @param ap the ArgParser to work with
* @param position the position in the parser to get.
* @return a Map mapping the String key to the value
*/
public static Map<String, Object> getMap(ArgParser ap, int position) {
PyObject arg = ap.getPyObject(position, Py.None);
if (Py.isInstance(arg, PyNone.TYPE)) {
return Collections.emptyMap();
}
Map<String, Object> ret = Maps.newHashMap();
// cast is safe as getPyObjectbyType ensures it
PyDictionary dict = (PyDictionary) arg;
PyList items = dict.items();
for (int x = 0; x < items.__len__(); x++) {
// It's a list of tuples
PyTuple item = (PyTuple) items.__getitem__(x);
// We call str(key) on the key to get the string and then convert it to the java string.
String key = (String) item.__getitem__(0).__str__().__tojava__(String.class);
PyObject value = item.__getitem__(1);
// Look up the conversion type and convert the value
Class<?> javaClass = PYOBJECT_TO_JAVA_OBJECT_MAP.get(value.getClass());
if (javaClass != null) {
ret.put(key, value.__tojava__(javaClass));
}
}
return ret;
}
private static PyObject convertObject(Object o) {
if (o instanceof String) {
return new PyString((String) o);
} else if (o instanceof Double) {
return new PyFloat((Double) o);
} else if (o instanceof Integer) {
return new PyInteger((Integer) o);
} else if (o instanceof Float) {
float f = (Float) o;
return new PyFloat(f);
}
return Py.None;
}
/**
* Convert the given Java Map into a PyDictionary.
*
* @param map the map to convert
* @return the python dictionary
*/
public static PyDictionary convertMapToDict(Map<String, Object> map) {
Map<PyObject, PyObject> resultMap = Maps.newHashMap();
for (Entry<String, Object> entry : map.entrySet()) {
resultMap.put(new PyString(entry.getKey()),
convertObject(entry.getValue()));
}
return new PyDictionary(resultMap);
}
}