Add in a collection of useful functions for interacting with the Jython Interpreter.
Change-Id: I1cef598fe8f057ed050452e4928196f815e574b5
diff --git a/tools/monkeyrunner/src/com/android/monkeyrunner/JythonUtils.java b/tools/monkeyrunner/src/com/android/monkeyrunner/JythonUtils.java
new file mode 100644
index 0000000..6fe46a2
--- /dev/null
+++ b/tools/monkeyrunner/src/com/android/monkeyrunner/JythonUtils.java
@@ -0,0 +1,162 @@
+/*
+ * 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.PyDictionary;
+import org.python.core.PyFloat;
+import org.python.core.PyInteger;
+import org.python.core.PyList;
+import org.python.core.PyObject;
+import org.python.core.PyString;
+import org.python.core.PyTuple;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Map;
+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);
+ builder.put(PyFloat.class, Float.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 = MonkeyRunner.class.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) {
+ // cast is safe as getPyObjectbyType ensures it
+ PyFloat object = (PyFloat) ap.getPyObjectByType(position, PyFloat.TYPE);
+ return object.asDouble();
+ }
+
+ /**
+ * Get a list of arguments from an ArgParser.
+ *
+ * @param <T> the type of list items to return
+ * @param ap the ArgParser
+ * @param position the position in the parser to get the argument from
+ * @param clz the type of items to return
+ * @return a list of those items
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> List<T> getList(ArgParser ap, int position, Class<?> clz) {
+ List<T> ret = Lists.newArrayList();
+ // cast is safe as getPyObjectbyType ensures it
+ PyList array = (PyList) ap.getPyObjectByType(position, PyList.TYPE);
+ for (int x = 0; x < array.__len__(); x++) {
+ T item = (T) array.__getitem__(x).__tojava__(clz);
+ ret.add(item);
+ }
+ return ret;
+ }
+
+ /**
+ * Get a dictionary from an ArgParser.
+ *
+ * @param ap the ArgParser to work with
+ * @param position the position in the parser to get.
+ * @return a Map mapping the String key to their underlying type.
+ */
+ public static Map<String, Object> getMap(ArgParser ap, int position) {
+ Map<String, Object> ret = Maps.newHashMap();
+ // cast is safe as getPyObjectbyType ensures it
+ PyDictionary dict = (PyDictionary) ap.getPyObjectByType(position, PyDictionary.TYPE);
+ PyList items = dict.items();
+ for (int x = 0; x < items.__len__(); x++) {
+ // It's a list of tuples
+ PyTuple item = (PyTuple) items.__getitem__(x);
+ String key = (String) item.__getitem__(0).__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;
+ }
+}