blob: 02c3d87acf2eaf0a98bc7e0d77b5aa6fc9feeceb [file] [log] [blame]
/*
* Copyright (C) 2014 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 android.hardware.camera2.dispatch;
import android.hardware.camera2.utils.UncheckedThrow;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import static com.android.internal.util.Preconditions.*;
/**
* Invoke a method on a dispatchable by its name (without knowing the {@code Method} ahead of time).
*
* @param <T> destination dispatch type, methods will be looked up in the class of {@code T}
*/
public class MethodNameInvoker<T> {
private final Dispatchable<T> mTarget;
private final Class<T> mTargetClass;
private final ConcurrentHashMap<String, Method> mMethods =
new ConcurrentHashMap<>();
/**
* Create a new method name invoker.
*
* @param target destination dispatch type, invokes will be redirected to this dispatcher
* @param targetClass destination dispatch class, the invoked methods will be from this class
*/
public MethodNameInvoker(Dispatchable<T> target, Class<T> targetClass) {
mTargetClass = targetClass;
mTarget = target;
}
/**
* Invoke a method by its name.
*
* <p>If more than one method exists in {@code targetClass}, the first method will be used.</p>
*
* @param methodName
* The name of the method, which will be matched 1:1 to the destination method
* @param params
* Variadic parameter list.
* @return
* The same kind of value that would normally be returned by calling {@code methodName}
* statically.
*
* @throws IllegalArgumentException if {@code methodName} does not exist on the target class
* @throws Throwable will rethrow anything that the target method would normally throw
*/
@SuppressWarnings("unchecked")
public <K> K invoke(String methodName, Object... params) {
checkNotNull(methodName, "methodName must not be null");
Method targetMethod = mMethods.get(methodName);
if (targetMethod == null) {
for (Method method : mTargetClass.getMethods()) {
// TODO future: match by # of params and types of params if possible
if (method.getName().equals(methodName)) {
targetMethod = method;
mMethods.put(methodName, targetMethod);
break;
}
}
if (targetMethod == null) {
throw new IllegalArgumentException(
"Method " + methodName + " does not exist on class " + mTargetClass);
}
}
try {
return (K) mTarget.dispatch(targetMethod, params);
} catch (Throwable e) {
UncheckedThrow.throwAnyException(e);
// unreachable
return null;
}
}
}