Coverage Summary for Class: AbstractInvocationHandler (com.google.common.reflect)

Class Class, % Method, % Line, %
AbstractInvocationHandler 100% (1/1) 57.1% (4/7) 84% (21/25)


1 /* 2  * Copyright (C) 2012 The Guava Authors 3  * 4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5  * in compliance with the License. You may obtain a copy of the License at 6  * 7  * http://www.apache.org/licenses/LICENSE-2.0 8  * 9  * Unless required by applicable law or agreed to in writing, software distributed under the License 10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11  * or implied. See the License for the specific language governing permissions and limitations under 12  * the License. 13  */ 14  15 package com.google.common.reflect; 16  17 import com.google.common.annotations.Beta; 18 import java.lang.reflect.InvocationHandler; 19 import java.lang.reflect.Method; 20 import java.lang.reflect.Proxy; 21 import java.util.Arrays; 22 import javax.annotation.CheckForNull; 23 import org.checkerframework.checker.nullness.qual.Nullable; 24  25 /** 26  * Abstract implementation of {@link InvocationHandler} that handles {@link Object#equals}, {@link 27  * Object#hashCode} and {@link Object#toString}. For example: 28  * 29  * <pre> 30  * class Unsupported extends AbstractInvocationHandler { 31  * protected Object handleInvocation(Object proxy, Method method, Object[] args) { 32  * throw new UnsupportedOperationException(); 33  * } 34  * } 35  * 36  * CharSequence unsupported = Reflection.newProxy(CharSequence.class, new Unsupported()); 37  * </pre> 38  * 39  * @author Ben Yu 40  * @since 12.0 41  */ 42 @Beta 43 // TODO(b/147136275): After adding @Nullable below, add . 44 @ElementTypesAreNonnullByDefault 45 public abstract class AbstractInvocationHandler implements InvocationHandler { 46  47  private static final Object[] NO_ARGS = {}; 48  49  /** 50  * {@inheritDoc} 51  * 52  * <ul> 53  * <li>{@code proxy.hashCode()} delegates to {@link AbstractInvocationHandler#hashCode} 54  * <li>{@code proxy.toString()} delegates to {@link AbstractInvocationHandler#toString} 55  * <li>{@code proxy.equals(argument)} returns true if: 56  * <ul> 57  * <li>{@code proxy} and {@code argument} are of the same type 58  * <li>and {@link AbstractInvocationHandler#equals} returns true for the {@link 59  * InvocationHandler} of {@code argument} 60  * </ul> 61  * <li>other method calls are dispatched to {@link #handleInvocation}. 62  * </ul> 63  */ 64  @Override 65  @CheckForNull 66  public final Object invoke(Object proxy, Method method, @CheckForNull @Nullable Object[] args) 67  throws Throwable { 68  if (args == null) { 69  args = NO_ARGS; 70  } 71  if (args.length == 0 && method.getName().equals("hashCode")) { 72  return hashCode(); 73  } 74  if (args.length == 1 75  && method.getName().equals("equals") 76  && method.getParameterTypes()[0] == Object.class) { 77  Object arg = args[0]; 78  if (arg == null) { 79  return false; 80  } 81  if (proxy == arg) { 82  return true; 83  } 84  return isProxyOfSameInterfaces(arg, proxy.getClass()) 85  && equals(Proxy.getInvocationHandler(arg)); 86  } 87  if (args.length == 0 && method.getName().equals("toString")) { 88  return toString(); 89  } 90  return handleInvocation(proxy, method, args); 91  } 92  93  /** 94  * {@link #invoke} delegates to this method upon any method invocation on the proxy instance, 95  * except {@link Object#equals}, {@link Object#hashCode} and {@link Object#toString}. The result 96  * will be returned as the proxied method's return value. 97  * 98  * <p>Unlike {@link #invoke}, {@code args} will never be null. When the method has no parameter, 99  * an empty array is passed in. 100  */ 101  @CheckForNull 102  protected abstract Object handleInvocation( 103  Object proxy, Method method, /* TODO(b/147136275): Add @Nullable. */ Object[] args) 104  throws Throwable; 105  106  /** 107  * By default delegates to {@link Object#equals} so instances are only equal if they are 108  * identical. {@code proxy.equals(argument)} returns true if: 109  * 110  * <ul> 111  * <li>{@code proxy} and {@code argument} are of the same type 112  * <li>and this method returns true for the {@link InvocationHandler} of {@code argument} 113  * </ul> 114  * 115  * <p>Subclasses can override this method to provide custom equality. 116  */ 117  @Override 118  public boolean equals(@CheckForNull Object obj) { 119  return super.equals(obj); 120  } 121  122  /** 123  * By default delegates to {@link Object#hashCode}. The dynamic proxies' {@code hashCode()} will 124  * delegate to this method. Subclasses can override this method to provide custom equality. 125  */ 126  @Override 127  public int hashCode() { 128  return super.hashCode(); 129  } 130  131  /** 132  * By default delegates to {@link Object#toString}. The dynamic proxies' {@code toString()} will 133  * delegate to this method. Subclasses can override this method to provide custom string 134  * representation for the proxies. 135  */ 136  @Override 137  public String toString() { 138  return super.toString(); 139  } 140  141  private static boolean isProxyOfSameInterfaces(Object arg, Class<?> proxyClass) { 142  return proxyClass.isInstance(arg) 143  // Equal proxy instances should mostly be instance of proxyClass 144  // Under some edge cases (such as the proxy of JDK types serialized and then deserialized) 145  // the proxy type may not be the same. 146  // We first check isProxyClass() so that the common case of comparing with non-proxy objects 147  // is efficient. 148  || (Proxy.isProxyClass(arg.getClass()) 149  && Arrays.equals(arg.getClass().getInterfaces(), proxyClass.getInterfaces())); 150  } 151 }