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 java.lang.reflect.Method;
19 
20 import static com.android.internal.util.Preconditions.*;
21 
22 /**
23  * A dispatcher that replaces one argument with another; replaces any argument at an index
24  * with another argument.
25  *
26  * <p>For example, we can override an {@code void onSomething(int x)} calls to have {@code x} always
27  * equal to 1. Or, if using this with a duck typing dispatcher, we could even overwrite {@code x} to
28  * be something
29  * that's not an {@code int}.</p>
30  *
31  * @param <T>
32  *          source dispatch type, whose methods with {@link #dispatch} will be called
33  * @param <TArg>
34  *          argument replacement type, args in {@link #dispatch} matching {@code argumentIndex}
35  *          will be overriden to objects of this type
36  */
37 public class ArgumentReplacingDispatcher<T, TArg> implements Dispatchable<T> {
38 
39     private final Dispatchable<T> mTarget;
40     private final int mArgumentIndex;
41     private final TArg mReplaceWith;
42 
43     /**
44      * Create a new argument replacing dispatcher; dispatches are forwarded to {@code target}
45      * after the argument is replaced.
46      *
47      * <p>For example, if a method {@code onAction(T1 a, Integer b, T2 c)} is invoked, and we wanted
48      * to replace all occurrences of {@code b} with {@code 0xDEADBEEF}, we would set
49      * {@code argumentIndex = 1} and {@code replaceWith = 0xDEADBEEF}.</p>
50      *
51      * <p>If a method dispatched has less arguments than {@code argumentIndex}, it is
52      * passed through with the arguments unchanged.</p>
53      *
54      * @param target destination dispatch type, methods will be redirected to this dispatcher
55      * @param argumentIndex the numeric index of the argument {@code >= 0}
56      * @param replaceWith arguments matching {@code argumentIndex} will be replaced with this object
57      */
ArgumentReplacingDispatcher(Dispatchable<T> target, int argumentIndex, TArg replaceWith)58     public ArgumentReplacingDispatcher(Dispatchable<T> target, int argumentIndex,
59             TArg replaceWith) {
60         mTarget = checkNotNull(target, "target must not be null");
61         mArgumentIndex = checkArgumentNonnegative(argumentIndex,
62                 "argumentIndex must not be negative");
63         mReplaceWith = checkNotNull(replaceWith, "replaceWith must not be null");
64     }
65 
66     @Override
dispatch(Method method, Object[] args)67     public Object dispatch(Method method, Object[] args) throws Throwable {
68 
69         if (args.length > mArgumentIndex) {
70             args = arrayCopy(args); // don't change in-place since it can affect upstream dispatches
71             args[mArgumentIndex] = mReplaceWith;
72         }
73 
74         return mTarget.dispatch(method, args);
75     }
76 
arrayCopy(Object[] array)77     private static Object[] arrayCopy(Object[] array) {
78         int length = array.length;
79         Object[] newArray = new Object[length];
80         for (int i = 0; i < length; ++i) {
81             newArray[i] = array[i];
82         }
83         return newArray;
84     }
85 }
86