19f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li/* 29f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Copyright (C) 2016 Google Inc. 39f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 49f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Licensed under the Apache License, Version 2.0 (the "License"); you may not 59f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * use this file except in compliance with the License. You may obtain a copy of 69f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * the License at 79f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 89f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * http://www.apache.org/licenses/LICENSE-2.0 99f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Unless required by applicable law or agreed to in writing, software 119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * License for the specific language governing permissions and limitations under 149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * the License. 159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */ 169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Lipackage com.googlecode.android_scripting.rpc; 189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.content.Intent; 209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.net.Uri; 219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.os.Bundle; 229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.os.Parcelable; 239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.facade.AndroidFacade; 259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.jsonrpc.RpcReceiver; 269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.jsonrpc.RpcReceiverManager; 279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.util.VisibleForTesting; 289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.lang.annotation.Annotation; 309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.lang.reflect.Constructor; 319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.lang.reflect.Method; 329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.lang.reflect.ParameterizedType; 339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.lang.reflect.Type; 349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.ArrayList; 359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.Collection; 369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.HashMap; 379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.List; 389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.Map; 399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport org.json.JSONArray; 419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport org.json.JSONException; 429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport org.json.JSONObject; 439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li/** 459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * An adapter that wraps {@code Method}. 469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @author igor.v.karp@gmail.com (Igor Karp) 489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */ 499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Lipublic final class MethodDescriptor { 509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private static final Map<Class<?>, Converter<?>> sConverters = populateConverters(); 519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private final Method mMethod; 539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private final Class<? extends RpcReceiver> mClass; 549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public MethodDescriptor(Class<? extends RpcReceiver> clazz, Method method) { 569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li mClass = clazz; 579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li mMethod = method; 589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li @Override 619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public String toString() { 629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return mMethod.getDeclaringClass().getCanonicalName() + "." + mMethod.getName(); 639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li /** Collects all methods with {@code RPC} annotation from given class. */ 669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public static Collection<MethodDescriptor> collectFrom(Class<? extends RpcReceiver> clazz) { 679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li List<MethodDescriptor> descriptors = new ArrayList<MethodDescriptor>(); 689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li for (Method method : clazz.getMethods()) { 699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (method.isAnnotationPresent(Rpc.class)) { 709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li descriptors.add(new MethodDescriptor(clazz, method)); 719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return descriptors; 749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li /** 779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Invokes the call that belongs to this object with the given parameters. Wraps the response 789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * (possibly an exception) in a JSONObject. 799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @param parameters 819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * {@code JSONArray} containing the parameters 829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @return result 839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @throws Throwable 849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */ 859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public Object invoke(RpcReceiverManager manager, final JSONArray parameters) throws Throwable { 869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li final Type[] parameterTypes = getGenericParameterTypes(); 889f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li final Object[] args = new Object[parameterTypes.length]; 899f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li final Annotation annotations[][] = getParameterAnnotations(); 909f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 919f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (parameters.length() > args.length) { 929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li throw new RpcError("Too many parameters specified."); 939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li for (int i = 0; i < args.length; i++) { 969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li final Type parameterType = parameterTypes[i]; 979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (i < parameters.length()) { 989f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li args[i] = convertParameter(parameters, i, parameterType); 999f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else if (MethodDescriptor.hasDefaultValue(annotations[i])) { 1009f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li args[i] = MethodDescriptor.getDefaultValue(parameterType, annotations[i]); 1019f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else { 1029f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li throw new RpcError("Argument " + (i + 1) + " is not present"); 1039f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 1049f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 1059f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 1069f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return invoke(manager, args); 1079f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 1089f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 1099f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li /** 1109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Invokes the call that belongs to this object with the given parameters. Wraps the response 1119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * (possibly an exception) in a JSONObject. 1129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 1139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @param parameters {@code Bundle} containing the parameters 1149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @return result 1159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @throws Throwable 1169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */ 1179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public Object invoke(RpcReceiverManager manager, final Bundle parameters) throws Throwable { 1189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li final Annotation annotations[][] = getParameterAnnotations(); 1199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li final Class<?>[] parameterTypes = getMethod().getParameterTypes(); 1209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li final Object[] args = new Object[parameterTypes.length]; 1219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 1229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li for (int i = 0; i < parameterTypes.length; i++) { 1239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Class<?> parameterType = parameterTypes[i]; 1249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li String parameterName = getName(annotations[i]); 1259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (i < parameterTypes.length) { 1269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li args[i] = convertParameter(parameters, parameterType, parameterName); 1279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else if (MethodDescriptor.hasDefaultValue(annotations[i])) { 1289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li args[i] = MethodDescriptor.getDefaultValue(parameterType, annotations[i]); 1299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else { 1309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li throw new RpcError("Argument " + (i + 1) + " is not present"); 1319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 1329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 1339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return invoke(manager, args); 1349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 1359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 1369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private Object invoke(RpcReceiverManager manager, Object[] args) throws Throwable{ 1379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Object result = null; 1389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li try { 1399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li result = manager.invoke(mClass, mMethod, args); 1409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } catch (Throwable t) { 1419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li throw t.getCause(); 1429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 1439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return result; 1449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 1459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 1469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li /** 1479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Converts a parameter from JSON into a Java Object. 1489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 1499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @return TODO 1509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */ 1519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li // TODO(damonkohler): This signature is a bit weird (auto-refactored). The obvious alternative 1529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li // would be to work on one supplied parameter and return the converted parameter. However, that's 1539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li // problematic because you lose the ability to call the getXXX methods on the JSON array. 1549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li @VisibleForTesting 1559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li static Object convertParameter(final JSONArray parameters, int index, Type type) 1569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li throws JSONException, RpcError { 1579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li try { 1589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li // Log.d("sl4a", parameters.toString()); 1599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li // Log.d("sl4a", type.toString()); 1609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li // We must handle null and numbers explicitly because we cannot magically cast them. We 1619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li // also need to convert implicitly from numbers to bools. 1629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (parameters.isNull(index)) { 1639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return null; 1649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else if (type == Boolean.class) { 1659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li try { 1669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return parameters.getBoolean(index); 1679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } catch (JSONException e) { 1689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return new Boolean(parameters.getInt(index) != 0); 1699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 1709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else if (type == Long.class) { 1719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return parameters.getLong(index); 1729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else if (type == Double.class) { 1739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return parameters.getDouble(index); 1749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else if (type == Integer.class) { 1759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return parameters.getInt(index); 1769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else if (type == Intent.class) { 1779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return buildIntent(parameters.getJSONObject(index)); 1789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else if (type == Integer[].class) { 1799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li JSONArray list = parameters.getJSONArray(index); 1809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Integer[] result = new Integer[list.length()]; 1819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li for (int i = 0; i < list.length(); i++) { 1829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li result[i] = list.getInt(i); 1839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 1849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return result; 185e0bd13ca95066315343234b1dcc6bffe734ffaa3Jakub Pawlowski } else if (type == byte[].class) { 186e0bd13ca95066315343234b1dcc6bffe734ffaa3Jakub Pawlowski JSONArray list = parameters.getJSONArray(index); 187e0bd13ca95066315343234b1dcc6bffe734ffaa3Jakub Pawlowski byte[] result = new byte[list.length()]; 188e0bd13ca95066315343234b1dcc6bffe734ffaa3Jakub Pawlowski for (int i = 0; i < list.length(); i++) { 189e0bd13ca95066315343234b1dcc6bffe734ffaa3Jakub Pawlowski result[i] = (byte)list.getInt(i); 190e0bd13ca95066315343234b1dcc6bffe734ffaa3Jakub Pawlowski } 191e0bd13ca95066315343234b1dcc6bffe734ffaa3Jakub Pawlowski return result; 1929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else if (type == String[].class) { 1939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li JSONArray list = parameters.getJSONArray(index); 1949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li String[] result = new String[list.length()]; 1959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li for (int i = 0; i < list.length(); i++) { 1969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li result[i] = list.getString(i); 1979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 1989f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return result; 1999f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else if (type == JSONObject.class) { 2009f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return parameters.getJSONObject(index); 2019f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else { 2029f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li // Magically cast the parameter to the right Java type. 2039f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return ((Class<?>) type).cast(parameters.get(index)); 2049f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2059f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } catch (ClassCastException e) { 2069f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li throw new RpcError("Argument " + (index + 1) + " should be of type " 2079f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li + ((Class<?>) type).getSimpleName() + "."); 2089f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2099f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 2119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private Object convertParameter(Bundle bundle, Class<?> type, String name) { 2129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Object param = null; 2139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (type.isAssignableFrom(Boolean.class)) { 2149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li param = bundle.getBoolean(name, false); 2159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (type.isAssignableFrom(Boolean[].class)) { 2179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li param = bundle.getBooleanArray(name); 2189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (type.isAssignableFrom(String.class)) { 2209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li param = bundle.getString(name); 2219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (type.isAssignableFrom(String[].class)) { 2239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li param = bundle.getStringArray(name); 2249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (type.isAssignableFrom(Integer.class)) { 2269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li param = bundle.getInt(name, 0); 2279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (type.isAssignableFrom(Integer[].class)) { 2299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li param = bundle.getIntArray(name); 2309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (type.isAssignableFrom(Bundle.class)) { 2329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li param = bundle.getBundle(name); 2339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (type.isAssignableFrom(Parcelable.class)) { 2359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li param = bundle.getParcelable(name); 2369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (type.isAssignableFrom(Parcelable[].class)) { 2389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li param = bundle.getParcelableArray(name); 2399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (type.isAssignableFrom(Intent.class)) { 2419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li param = bundle.getParcelable(name); 2429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return param; 2449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 2469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public static Object buildIntent(JSONObject jsonObject) throws JSONException { 2479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Intent intent = new Intent(); 2489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (jsonObject.has("action")) { 2499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li intent.setAction(jsonObject.getString("action")); 2509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (jsonObject.has("data") && jsonObject.has("type")) { 2529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li intent.setDataAndType(Uri.parse(jsonObject.optString("data", null)), 2539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li jsonObject.optString("type", null)); 2549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else if (jsonObject.has("data")) { 2559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li intent.setData(Uri.parse(jsonObject.optString("data", null))); 2569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else if (jsonObject.has("type")) { 2579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li intent.setType(jsonObject.optString("type", null)); 2589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (jsonObject.has("packagename") && jsonObject.has("classname")) { 2609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li intent.setClassName(jsonObject.getString("packagename"), jsonObject.getString("classname")); 2619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (jsonObject.has("flags")) { 2639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li intent.setFlags(jsonObject.getInt("flags")); 2649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (!jsonObject.isNull("extras")) { 2669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li AndroidFacade.putExtrasFromJsonObject(jsonObject.getJSONObject("extras"), intent); 2679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (!jsonObject.isNull("categories")) { 2699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li JSONArray categories = jsonObject.getJSONArray("categories"); 2709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li for (int i = 0; i < categories.length(); i++) { 2719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li intent.addCategory(categories.getString(i)); 2729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return intent; 2759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 2779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public Method getMethod() { 2789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return mMethod; 2799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 2819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public Class<? extends RpcReceiver> getDeclaringClass() { 2829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return mClass; 2839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 2859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public String getName() { 2869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (mMethod.isAnnotationPresent(RpcName.class)) { 2879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return mMethod.getAnnotation(RpcName.class).name(); 2889f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2899f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return mMethod.getName(); 2909f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2919f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 2929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public Type[] getGenericParameterTypes() { 2939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return mMethod.getGenericParameterTypes(); 2949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 2969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public Annotation[][] getParameterAnnotations() { 2979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return mMethod.getParameterAnnotations(); 2989f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2999f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 3009f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li /** 3019f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Returns a human-readable help text for this RPC, based on annotations in the source code. 3029f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 3039f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @return derived help string 3049f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */ 3059f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public String getHelp() { 3069f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li StringBuilder helpBuilder = new StringBuilder(); 3079f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Rpc rpcAnnotation = mMethod.getAnnotation(Rpc.class); 3089f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 3099f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li helpBuilder.append(mMethod.getName()); 3109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li helpBuilder.append("("); 3119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li final Class<?>[] parameterTypes = mMethod.getParameterTypes(); 3129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li final Type[] genericParameterTypes = mMethod.getGenericParameterTypes(); 3139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li final Annotation[][] annotations = mMethod.getParameterAnnotations(); 3149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li for (int i = 0; i < parameterTypes.length; i++) { 3159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (i == 0) { 3169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li helpBuilder.append("\n "); 3179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else { 3189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li helpBuilder.append(",\n "); 3199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 3219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li helpBuilder.append(getHelpForParameter(genericParameterTypes[i], annotations[i])); 3229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li helpBuilder.append(")\n\n"); 3249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li helpBuilder.append(rpcAnnotation.description()); 3259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (!rpcAnnotation.returns().equals("")) { 3269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li helpBuilder.append("\n"); 3279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li helpBuilder.append("\nReturns:\n "); 3289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li helpBuilder.append(rpcAnnotation.returns()); 3299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 3319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (mMethod.isAnnotationPresent(RpcStartEvent.class)) { 3329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li String eventName = mMethod.getAnnotation(RpcStartEvent.class).value(); 3339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li helpBuilder.append(String.format("\n\nGenerates \"%s\" events.", eventName)); 3349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 3369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (mMethod.isAnnotationPresent(RpcDeprecated.class)) { 3379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li String replacedBy = mMethod.getAnnotation(RpcDeprecated.class).value(); 3389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li String release = mMethod.getAnnotation(RpcDeprecated.class).release(); 3399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li helpBuilder.append(String.format("\n\nDeprecated in %s! Please use %s instead.", release, 3409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li replacedBy)); 3419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 3439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return helpBuilder.toString(); 3449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 3469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li /** 3479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Returns the help string for one particular parameter. This respects optional parameters. 3489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 3499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @param parameterType 3509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * (generic) type of the parameter 3519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @param annotations 3529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * annotations of the parameter, may be null 3539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @return string describing the parameter based on source code annotations 3549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */ 3559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private static String getHelpForParameter(Type parameterType, Annotation[] annotations) { 3569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li StringBuilder result = new StringBuilder(); 3579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 3589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li appendTypeName(result, parameterType); 3599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li result.append(" "); 3609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li result.append(getName(annotations)); 3619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (hasDefaultValue(annotations)) { 3629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li result.append("[optional"); 3639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (hasExplicitDefaultValue(annotations)) { 3649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li result.append(", default " + getDefaultValue(parameterType, annotations)); 3659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li result.append("]"); 3679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 3699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li String description = getDescription(annotations); 3709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (description.length() > 0) { 3719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li result.append(": "); 3729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li result.append(description); 3739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 3759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return result.toString(); 3769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 3789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li /** 3799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Appends the name of the given type to the {@link StringBuilder}. 3809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 3819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @param builder 3829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * string builder to append to 3839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @param type 3849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * type whose name to append 3859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */ 3869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private static void appendTypeName(final StringBuilder builder, final Type type) { 3879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (type instanceof Class<?>) { 3889f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li builder.append(((Class<?>) type).getSimpleName()); 3899f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else { 3909f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li ParameterizedType parametrizedType = (ParameterizedType) type; 3919f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li builder.append(((Class<?>) parametrizedType.getRawType()).getSimpleName()); 3929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li builder.append("<"); 3939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 3949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Type[] arguments = parametrizedType.getActualTypeArguments(); 3959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li for (int i = 0; i < arguments.length; i++) { 3969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (i > 0) { 3979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li builder.append(", "); 3989f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3999f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li appendTypeName(builder, arguments[i]); 4009f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 4019f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li builder.append(">"); 4029f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 4039f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 4049f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 4059f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li /** 4069f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Returns parameter descriptors suitable for the RPC call text representation. 4079f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 4089f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * <p> 4099f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Uses parameter value, default value or name, whatever is available first. 4109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 4119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @return an array of parameter descriptors 4129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */ 4139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public ParameterDescriptor[] getParameterValues(String[] values) { 4149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Type[] parameterTypes = mMethod.getGenericParameterTypes(); 4159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Annotation[][] parametersAnnotations = mMethod.getParameterAnnotations(); 4169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li ParameterDescriptor[] parameters = new ParameterDescriptor[parametersAnnotations.length]; 4179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li for (int index = 0; index < parameters.length; index++) { 4189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li String value; 4199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (index < values.length) { 4209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li value = values[index]; 4219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else if (hasDefaultValue(parametersAnnotations[index])) { 4229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Object defaultValue = getDefaultValue(parameterTypes[index], parametersAnnotations[index]); 4239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (defaultValue == null) { 4249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li value = null; 4259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else { 4269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li value = String.valueOf(defaultValue); 4279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 4289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else { 4299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li value = getName(parametersAnnotations[index]); 4309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 4319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li parameters[index] = new ParameterDescriptor(value, parameterTypes[index]); 4329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 4339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return parameters; 4349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 4359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 4369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li /** 4379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Returns parameter hints. 4389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 4399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @return an array of parameter hints 4409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */ 4419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public String[] getParameterHints() { 4429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Annotation[][] parametersAnnotations = mMethod.getParameterAnnotations(); 4439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li String[] hints = new String[parametersAnnotations.length]; 4449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li for (int index = 0; index < hints.length; index++) { 4459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li String name = getName(parametersAnnotations[index]); 4469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li String description = getDescription(parametersAnnotations[index]); 4479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li String hint = "No paramenter description."; 4489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (!name.equals("") && !description.equals("")) { 4499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li hint = name + ": " + description; 4509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else if (!name.equals("")) { 4519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li hint = name; 4529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else if (!description.equals("")) { 4539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li hint = description; 4549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 4559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li hints[index] = hint; 4569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 4579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return hints; 4589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 4599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 4609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li /** 4619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Extracts the formal parameter name from an annotation. 4629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 4639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @param annotations 4649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * the annotations of the parameter 4659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @return the formal name of the parameter 4669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */ 4679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private static String getName(Annotation[] annotations) { 4689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li for (Annotation a : annotations) { 4699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (a instanceof RpcParameter) { 4709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return ((RpcParameter) a).name(); 4719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 4729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 4739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li throw new IllegalStateException("No parameter name"); 4749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 4759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 4769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li /** 4779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Extracts the parameter description from its annotations. 4789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 4799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @param annotations 4809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * the annotations of the parameter 4819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @return the description of the parameter 4829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */ 4839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private static String getDescription(Annotation[] annotations) { 4849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li for (Annotation a : annotations) { 4859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (a instanceof RpcParameter) { 4869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return ((RpcParameter) a).description(); 4879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 4889f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 4899f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li throw new IllegalStateException("No parameter description"); 4909f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 4919f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 4929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li /** 4939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Returns the default value for a specific parameter. 4949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 4959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @param parameterType 4969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * parameterType 4979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @param annotations 4989f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * annotations of the parameter 4999f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */ 5009f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public static Object getDefaultValue(Type parameterType, Annotation[] annotations) { 5019f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li for (Annotation a : annotations) { 5029f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (a instanceof RpcDefault) { 5039f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li RpcDefault defaultAnnotation = (RpcDefault) a; 5049f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Converter<?> converter = converterFor(parameterType, defaultAnnotation.converter()); 5059f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return converter.convert(defaultAnnotation.value()); 5069f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else if (a instanceof RpcOptional) { 5079f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return null; 5089f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5099f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li throw new IllegalStateException("No default value for " + parameterType); 5119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 5139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li @SuppressWarnings("rawtypes") 5149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private static Converter<?> converterFor(Type parameterType, 5159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Class<? extends Converter> converterClass) { 5169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (converterClass == Converter.class) { 5179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Converter<?> converter = sConverters.get(parameterType); 5189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (converter == null) { 5199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li throw new IllegalArgumentException("No predefined converter found for " + parameterType); 5209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return converter; 5229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li try { 5249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Constructor<?> constructor = converterClass.getConstructor(new Class<?>[0]); 5259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return (Converter<?>) constructor.newInstance(new Object[0]); 5269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } catch (Exception e) { 5279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li throw new IllegalArgumentException("Cannot create converter from " 5289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li + converterClass.getCanonicalName()); 5299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 5329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li /** 5339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Determines whether or not this parameter has default value. 5349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 5359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @param annotations 5369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * annotations of the parameter 5379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */ 5389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public static boolean hasDefaultValue(Annotation[] annotations) { 5399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li for (Annotation a : annotations) { 5409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (a instanceof RpcDefault || a instanceof RpcOptional) { 5419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return true; 5429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return false; 5459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 5479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li /** 5489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Returns whether the default value is specified for a specific parameter. 5499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 5509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * @param annotations 5519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * annotations of the parameter 5529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */ 5539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li @VisibleForTesting 5549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li static boolean hasExplicitDefaultValue(Annotation[] annotations) { 5559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li for (Annotation a : annotations) { 5569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (a instanceof RpcDefault) { 5579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return true; 5589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return false; 5619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 5639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li /** Returns the converters for {@code String}, {@code Integer} and {@code Boolean}. */ 5649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private static Map<Class<?>, Converter<?>> populateConverters() { 5659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Map<Class<?>, Converter<?>> converters = new HashMap<Class<?>, Converter<?>>(); 5669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li converters.put(String.class, new Converter<String>() { 5679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li @Override 5689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public String convert(String value) { 5699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return value; 5709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li }); 5729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li converters.put(Integer.class, new Converter<Integer>() { 5739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li @Override 5749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public Integer convert(String input) { 5759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li try { 5769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return Integer.decode(input); 5779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } catch (NumberFormatException e) { 5789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li throw new IllegalArgumentException("'" + input + "' is not an integer"); 5799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li }); 5829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li converters.put(Boolean.class, new Converter<Boolean>() { 5839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li @Override 5849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public Boolean convert(String input) { 5859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (input == null) { 5869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return null; 5879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5889f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li input = input.toLowerCase(); 5899f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (input.equals("true")) { 5909f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return Boolean.TRUE; 5919f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (input.equals("false")) { 5939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return Boolean.FALSE; 5949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li throw new IllegalArgumentException("'" + input + "' is not a boolean"); 5969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 5979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li }); 5989f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return converters; 5999f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 6009f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li} 601