• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 
17 package com.android.dx.mockito.inline;
18 
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.util.concurrent.Callable;
22 import java.util.concurrent.ConcurrentHashMap;
23 import java.util.concurrent.ConcurrentMap;
24 
25 /**
26  * Called by method entry hooks. Dispatches these hooks to the {@code MockMethodAdvice}.
27  */
28 @SuppressWarnings("unused")
29 public class MockMethodDispatcher {
30     // An instance of {@code MockMethodAdvice}
31     private Object mAdvice;
32 
33     // All dispatchers for various identifiers
34     private static final ConcurrentMap<String, MockMethodDispatcher> INSTANCE =
35             new ConcurrentHashMap<>();
36 
37     /**
38      * Get the dispatcher for a identifier.
39      *
40      * @param identifier identifier of the dispatcher
41      * @param instance instance that might be mocked
42      *
43      * @return dispatcher for the identifier
44      */
get(String identifier, Object instance)45     public static MockMethodDispatcher get(String identifier, Object instance) {
46         if (instance == INSTANCE) {
47             // Avoid endless loop if ConcurrentHashMap was redefined to check for being a mock.
48             return null;
49         } else {
50             return INSTANCE.get(identifier);
51         }
52     }
53 
54     /**
55      * Create a new dispatcher.
56      *
57      * @param advice Advice the dispatcher should call
58      */
MockMethodDispatcher(Object advice)59     private MockMethodDispatcher(Object advice) {
60         mAdvice = advice;
61     }
62 
63     /**
64      * Set up a new advice to receive calls for an identifier
65      *
66      * @param identifier a unique identifier
67      * @param advice advice the dispatcher should call
68      */
set(String identifier, Object advice)69     public static void set(String identifier, Object advice) {
70         INSTANCE.putIfAbsent(identifier, new MockMethodDispatcher(advice));
71     }
72 
73     /**
74      * Calls {@code MockMethodAdvice#handle}
75      */
handle(Object instance, Method origin, Object[] arguments)76     public Callable<?> handle(Object instance, Method origin, Object[] arguments) throws Throwable {
77         try {
78             return (Callable<?>) mAdvice.getClass().getMethod("handle", Object.class, Method.class,
79                     Object[].class).invoke(mAdvice, instance, origin, arguments);
80         } catch (InvocationTargetException e) {
81             throw e.getCause();
82         }
83     }
84 
85     /**
86      * Calls {@code MockMethodAdvice#isMock}
87      */
isMock(Object instance)88     public boolean isMock(Object instance) {
89         try {
90             return (Boolean) mAdvice.getClass().getMethod("isMock", Object.class).invoke(mAdvice,
91                     instance);
92         } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
93             throw new IllegalStateException(e);
94         }
95     }
96 
97     /**
98      * Calls {@code MockMethodAdvice#isMocked}
99      */
isMocked(Object instance)100     public boolean isMocked(Object instance) {
101         try {
102             return (Boolean) mAdvice.getClass().getMethod("isMocked", Object.class).invoke(mAdvice,
103                     instance);
104         } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
105             throw new IllegalStateException(e);
106         }
107     }
108 
109     /**
110      * Calls {@code MockMethodAdvice#isOverridden}
111      */
isOverridden(Object instance, Method origin)112     public boolean isOverridden(Object instance, Method origin) {
113         try {
114             return (Boolean) mAdvice.getClass().getMethod("isOverridden", Object.class,
115                     Method.class).invoke(mAdvice, instance, origin);
116         } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
117             throw new IllegalStateException(e);
118         }
119     }
120 
121     /**
122      * Calls {@code MockMethodAdvice#getOrigin}
123      */
getOrigin(Object mock, String instrumentedMethodWithTypeAndSignature)124     public Method getOrigin(Object mock, String instrumentedMethodWithTypeAndSignature)
125             throws Throwable {
126         return (Method) mAdvice.getClass().getMethod("getOrigin", Object.class,
127                 String.class).invoke(mAdvice, mock, instrumentedMethodWithTypeAndSignature);
128     }
129 }
130