1 /* 2 * Copyright (C) 2012 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 com.android.dx.stock.ProxyBuilder; 20 21 import org.mockito.Mockito; 22 import org.mockito.invocation.InvocationFactory.RealMethodBehavior; 23 import org.mockito.invocation.MockHandler; 24 import org.mockito.mock.MockCreationSettings; 25 26 import java.lang.reflect.InvocationHandler; 27 import java.lang.reflect.Method; 28 29 import static org.mockito.Mockito.withSettings; 30 31 /** 32 * Handles proxy and entry hook method invocations added by 33 * {@link InlineDexmakerMockMaker#createMock(MockCreationSettings, MockHandler)} 34 */ 35 final class InvocationHandlerAdapter implements InvocationHandler { 36 private MockHandler handler; 37 InvocationHandlerAdapter(MockHandler handler)38 InvocationHandlerAdapter(MockHandler handler) { 39 this.handler = handler; 40 } 41 isEqualsMethod(Method method)42 private static boolean isEqualsMethod(Method method) { 43 return method.getName().equals("equals") 44 && method.getParameterTypes().length == 1 45 && method.getParameterTypes()[0] == Object.class; 46 } 47 isHashCodeMethod(Method method)48 private static boolean isHashCodeMethod(Method method) { 49 return method.getName().equals("hashCode") 50 && method.getParameterTypes().length == 0; 51 } 52 53 /** 54 * Intercept a method call. Called <u>before</u> a method is called by the method entry hook. 55 * 56 * <p>This does the same as {@link #invoke(Object, Method, Object[])} but this handles methods 57 * that got and entry hook. 58 * 59 * @param mock mocked object 60 * @param method method that was called 61 * @param rawArgs arguments to the method 62 * @param superMethod The super method 63 * 64 * @return mocked result 65 * @throws Throwable An exception if thrown 66 */ interceptEntryHook(final Object mock, final Method method, final Object[] rawArgs, final SuperMethod superMethod)67 Object interceptEntryHook(final Object mock, final Method method, final Object[] rawArgs, 68 final SuperMethod superMethod) throws Throwable { 69 // args can be null if the method invoked has no arguments, but Mockito expects a non-null 70 Object[] args = rawArgs; 71 if (rawArgs == null) { 72 args = new Object[0]; 73 } 74 75 return handler.handle(Mockito.framework().getInvocationFactory().createInvocation(mock, 76 withSettings().build(mock.getClass()), method, new RealMethodBehavior() { 77 @Override 78 public Object call() throws Throwable { 79 return superMethod.invoke(); 80 } 81 }, args)); 82 } 83 84 /** 85 * Intercept a method call. Called <u>before</u> a method is called by the proxied method. 86 * 87 * <p>This does the same as {@link #interceptEntryHook(Object, Method, Object[], SuperMethod)} 88 * but this handles proxied methods. We only proxy abstract methods. 89 * 90 * @param proxy proxies object 91 * @param method method that was called 92 * @param rawArgs arguments to the method 93 * 94 * @return mocked result 95 * @throws Throwable An exception if thrown 96 */ 97 @Override 98 public Object invoke(final Object proxy, final Method method, final Object[] rawArgs) throws 99 Throwable { 100 // args can be null if the method invoked has no arguments, but Mockito expects a non-null 101 Object[] args = rawArgs; 102 if (rawArgs == null) { 103 args = new Object[0]; 104 } 105 106 if (isEqualsMethod(method)) { 107 return proxy == args[0]; 108 } else if (isHashCodeMethod(method)) { 109 return System.identityHashCode(proxy); 110 } 111 112 return handler.handle(Mockito.framework().getInvocationFactory().createInvocation(proxy, 113 withSettings().build(proxy.getClass().getSuperclass()), method, 114 new RealMethodBehavior() { 115 @Override 116 public Object call() throws Throwable { 117 return ProxyBuilder.callSuper(proxy, method, rawArgs); 118 } 119 }, args)); 120 } 121 122 /** 123 * Get the handler registered with this adapter. 124 * 125 * @return handler 126 */ 127 MockHandler getHandler() { 128 return handler; 129 } 130 131 /** 132 * Set a new handler for this adapter. 133 */ 134 void setHandler(MockHandler handler) { 135 this.handler = handler; 136 } 137 138 /** 139 * Interface used to describe a supermethod that can be called. 140 */ 141 interface SuperMethod { 142 Object invoke() throws Throwable; 143 } 144 } 145