1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.hardware.camera2.dispatch; 17 18 import static com.android.internal.util.Preconditions.checkNotNull; 19 20 import android.hardware.camera2.utils.UncheckedThrow; 21 22 import java.lang.reflect.Method; 23 import java.util.concurrent.ConcurrentHashMap; 24 25 /** 26 * Invoke a method on a dispatchable by its name (without knowing the {@code Method} ahead of time). 27 * 28 * @param <T> destination dispatch type, methods will be looked up in the class of {@code T} 29 */ 30 public class MethodNameInvoker<T> { 31 32 private final Dispatchable<T> mTarget; 33 private final Class<T> mTargetClass; 34 private final Method[] mTargetClassMethods; 35 private final ConcurrentHashMap<String, Method> mMethods = 36 new ConcurrentHashMap<>(); 37 38 /** 39 * Create a new method name invoker. 40 * 41 * @param target destination dispatch type, invokes will be redirected to this dispatcher 42 * @param targetClass destination dispatch class, the invoked methods will be from this class 43 */ MethodNameInvoker(Dispatchable<T> target, Class<T> targetClass)44 public MethodNameInvoker(Dispatchable<T> target, Class<T> targetClass) { 45 mTargetClass = targetClass; 46 mTargetClassMethods = targetClass.getMethods(); 47 mTarget = target; 48 } 49 50 /** 51 * Invoke a method by its name. 52 * 53 * <p>If more than one method exists in {@code targetClass}, the first method with the right 54 * number of arguments will be used, and later calls will all use that method.</p> 55 * 56 * @param methodName 57 * The name of the method, which will be matched 1:1 to the destination method 58 * @param params 59 * Variadic parameter list. 60 * @return 61 * The same kind of value that would normally be returned by calling {@code methodName} 62 * statically. 63 * 64 * @throws IllegalArgumentException if {@code methodName} does not exist on the target class 65 * @throws Throwable will rethrow anything that the target method would normally throw 66 */ 67 @SuppressWarnings("unchecked") invoke(String methodName, Object... params)68 public <K> K invoke(String methodName, Object... params) { 69 checkNotNull(methodName, "methodName must not be null"); 70 71 Method targetMethod = mMethods.get(methodName); 72 if (targetMethod == null) { 73 for (Method method : mTargetClassMethods) { 74 // TODO future: match types of params if possible 75 if (method.getName().equals(methodName) && 76 (params.length == method.getParameterTypes().length) ) { 77 targetMethod = method; 78 mMethods.put(methodName, targetMethod); 79 break; 80 } 81 } 82 83 if (targetMethod == null) { 84 throw new IllegalArgumentException( 85 "Method " + methodName + " does not exist on class " + mTargetClass); 86 } 87 } 88 89 try { 90 return (K) mTarget.dispatch(targetMethod, params); 91 } catch (Throwable e) { 92 UncheckedThrow.throwAnyException(e); 93 // unreachable 94 return null; 95 } 96 } 97 } 98