/*
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.stetho.inspector.elements.android;
import com.facebook.stetho.common.LogUtil;
import com.facebook.stetho.common.Util;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
/**
* Tries to arbitrarily invoke single argument methods by name on an object instance by trying out
* different argument types.
*/
public class MethodInvoker {
private static final List<TypedMethodInvoker<?>> invokers = Arrays.asList(
new StringMethodInvoker(), new CharSequenceMethodInvoker(), new IntegerMethodInvoker(),
new FloatMethodInvoker(), new BooleanMethodInvoker());
/**
* Tries to invoke a method on receiver with a single argument by trying out different types
* for arg until it finds one that matches (or not). No exceptions are thrown on failure.
*
* @param methodName The method name to be invoked
* @param argument The single argument to be provided to the method
*/
public void invoke(Object receiver, String methodName, String argument) {
Util.throwIfNull(receiver, methodName, argument);
int size = invokers.size();
for (int i = 0; i < size; ++i) {
final TypedMethodInvoker<?> invoker = invokers.get(i);
if (invoker.invoke(receiver, methodName, argument)) {
return;
}
}
LogUtil.w("Method with name " + methodName +
" not found for any of the MethodInvoker supported argument types.");
}
private static abstract class TypedMethodInvoker<T> {
private final Class<T> mArgType;
TypedMethodInvoker(Class<T> argType) {
mArgType = argType;
}
boolean invoke(Object receiver, String methodName, String argument) {
try {
Method method = receiver.getClass().getMethod(methodName, mArgType);
method.invoke(receiver, convertArgument(argument));
return true;
} catch (NoSuchMethodException ignored) {
// ignore
} catch (InvocationTargetException e) {
LogUtil.w("InvocationTargetException: " + e.getMessage());
} catch (IllegalAccessException e) {
LogUtil.w("IllegalAccessException: " + e.getMessage());
} catch (IllegalArgumentException e) {
LogUtil.w("IllegalArgumentException: " + e.getMessage());
}
return false;
}
abstract T convertArgument(String argument);
}
private static class StringMethodInvoker extends TypedMethodInvoker<String> {
StringMethodInvoker() {
super(String.class);
}
@Override
String convertArgument(String argument) {
return argument;
}
}
private static class CharSequenceMethodInvoker extends TypedMethodInvoker<CharSequence> {
CharSequenceMethodInvoker() {
super(CharSequence.class);
}
@Override
CharSequence convertArgument(String argument) {
return argument;
}
}
private static class IntegerMethodInvoker extends TypedMethodInvoker<Integer> {
IntegerMethodInvoker() {
super(int.class);
}
@Override
Integer convertArgument(String argument) {
return Integer.parseInt(argument);
}
}
private static class FloatMethodInvoker extends TypedMethodInvoker<Float> {
FloatMethodInvoker() {
super(float.class);
}
@Override
Float convertArgument(String argument) {
return Float.parseFloat(argument);
}
}
private static class BooleanMethodInvoker extends TypedMethodInvoker<Boolean> {
BooleanMethodInvoker() {
super(boolean.class);
}
@Override
Boolean convertArgument(String argument) {
return Boolean.parseBoolean(argument);
}
}
}