View Javadoc
1   /*
2    * MIT License
3    * Copyright (c) 2006-2025 JMockit developers
4    * See LICENSE file for full license text.
5    */
6   package mockit.internal.reflection;
7   
8   import edu.umd.cs.findbugs.annotations.NonNull;
9   import edu.umd.cs.findbugs.annotations.Nullable;
10  
11  import java.lang.annotation.Annotation;
12  import java.lang.reflect.Constructor;
13  import java.lang.reflect.InvocationHandler;
14  import java.lang.reflect.Method;
15  
16  import mockit.internal.util.DefaultValues;
17  import mockit.internal.util.ObjectMethods;
18  
19  /**
20   * Handles invocations to all kinds of mock implementations created for interfaces and annotation types through any of
21   * the mocking APIs.
22   * <p>
23   * The <code>java.lang.Object</code> methods <code>equals</code>, <code>hashCode</code>, and <code>toString</code> are
24   * handled in a meaningful way, returning a value that makes sense for the proxy instance. The special
25   * {@linkplain Annotation} contracts for these three methods is <em>not</em> observed, though, since it would require
26   * making dynamic calls to the mocked annotation attributes.
27   * <p>
28   * Any other method invocation is handled by simply returning the default value according to the method's return type
29   * (as defined in {@linkplain DefaultValues}).
30   */
31  public final class MockInvocationHandler implements InvocationHandler {
32      public static final InvocationHandler INSTANCE = new MockInvocationHandler();
33      private static final Class<?>[] CONSTRUCTOR_PARAMETERS_FOR_PROXY_CLASS = { InvocationHandler.class };
34  
35      @NonNull
36      public static Object newMockedInstance(@NonNull Class<?> proxyClass) {
37          Constructor<?> publicConstructor;
38          try {
39              publicConstructor = proxyClass.getConstructor(CONSTRUCTOR_PARAMETERS_FOR_PROXY_CLASS);
40          } catch (NoSuchMethodException e) {
41              throw new RuntimeException(e);
42          }
43  
44          return ConstructorReflection.invokeAccessible(publicConstructor, INSTANCE);
45      }
46  
47      @Nullable
48      @Override
49      public Object invoke(@NonNull Object proxy, @NonNull Method method, @Nullable Object[] args) {
50          Class<?> declaringClass = method.getDeclaringClass();
51          String methodName = method.getName();
52  
53          if (declaringClass == Object.class) {
54              if ("equals".equals(methodName)) {
55                  return args != null && args.length > 0 && proxy == args[0];
56              }
57              if ("hashCode".equals(methodName)) {
58                  return System.identityHashCode(proxy);
59              }
60              // "toString"
61              return ObjectMethods.objectIdentity(proxy);
62          }
63  
64          if (declaringClass == Annotation.class) {
65              return proxy.getClass().getInterfaces()[0];
66          }
67  
68          Class<?> retType = method.getReturnType();
69          return DefaultValues.computeForType(retType);
70      }
71  }