Home | History | Annotate | Download | only in dispatch
      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 android.hardware.camera2.utils.UncheckedThrow;
     19 
     20 import java.lang.reflect.Method;
     21 import java.util.concurrent.ConcurrentHashMap;
     22 
     23 import static com.android.internal.util.Preconditions.*;
     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 ConcurrentHashMap<String, Method> mMethods =
     35             new ConcurrentHashMap<>();
     36 
     37     /**
     38      * Create a new method name invoker.
     39      *
     40      * @param target destination dispatch type, invokes will be redirected to this dispatcher
     41      * @param targetClass destination dispatch class, the invoked methods will be from this class
     42      */
     43     public MethodNameInvoker(Dispatchable<T> target, Class<T> targetClass) {
     44         mTargetClass = targetClass;
     45         mTarget = target;
     46     }
     47 
     48     /**
     49      * Invoke a method by its name.
     50      *
     51      * <p>If more than one method exists in {@code targetClass}, the first method with the right
     52      * number of arguments will be used, and later calls will all use that method.</p>
     53      *
     54      * @param methodName
     55      *          The name of the method, which will be matched 1:1 to the destination method
     56      * @param params
     57      *          Variadic parameter list.
     58      * @return
     59      *          The same kind of value that would normally be returned by calling {@code methodName}
     60      *          statically.
     61      *
     62      * @throws IllegalArgumentException if {@code methodName} does not exist on the target class
     63      * @throws Throwable will rethrow anything that the target method would normally throw
     64      */
     65     @SuppressWarnings("unchecked")
     66     public <K> K invoke(String methodName, Object... params) {
     67         checkNotNull(methodName, "methodName must not be null");
     68 
     69         Method targetMethod = mMethods.get(methodName);
     70         if (targetMethod == null) {
     71             for (Method method : mTargetClass.getMethods()) {
     72                 // TODO future: match types of params if possible
     73                 if (method.getName().equals(methodName) &&
     74                         (params.length == method.getParameterTypes().length) ) {
     75                     targetMethod = method;
     76                     mMethods.put(methodName, targetMethod);
     77                     break;
     78                 }
     79             }
     80 
     81             if (targetMethod == null) {
     82                 throw new IllegalArgumentException(
     83                         "Method " + methodName + " does not exist on class " + mTargetClass);
     84             }
     85         }
     86 
     87         try {
     88             return (K) mTarget.dispatch(targetMethod, params);
     89         } catch (Throwable e) {
     90             UncheckedThrow.throwAnyException(e);
     91             // unreachable
     92             return null;
     93         }
     94     }
     95 }
     96