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 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      */
     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")
     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