001/*
002 * Copyright (C) 2012 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 */
014
015package com.google.common.reflect;
016
017import java.lang.reflect.InvocationHandler;
018import java.lang.reflect.Method;
019import java.lang.reflect.Proxy;
020import java.util.Arrays;
021import javax.annotation.CheckForNull;
022import org.checkerframework.checker.nullness.qual.Nullable;
023
024/**
025 * Abstract implementation of {@link InvocationHandler} that handles {@link Object#equals}, {@link
026 * Object#hashCode} and {@link Object#toString}. For example:
027 *
028 * <pre>
029 * class Unsupported extends AbstractInvocationHandler {
030 *   protected Object handleInvocation(Object proxy, Method method, Object[] args) {
031 *     throw new UnsupportedOperationException();
032 *   }
033 * }
034 *
035 * CharSequence unsupported = Reflection.newProxy(CharSequence.class, new Unsupported());
036 * </pre>
037 *
038 * @author Ben Yu
039 * @since 12.0
040 */
041@ElementTypesAreNonnullByDefault
042public abstract class AbstractInvocationHandler implements InvocationHandler {
043  /** Constructor for use by subclasses. */
044  public AbstractInvocationHandler() {}
045
046  private static final Object[] NO_ARGS = {};
047
048  /**
049   * {@inheritDoc}
050   *
051   * <ul>
052   *   <li>{@code proxy.hashCode()} delegates to {@link AbstractInvocationHandler#hashCode}
053   *   <li>{@code proxy.toString()} delegates to {@link AbstractInvocationHandler#toString}
054   *   <li>{@code proxy.equals(argument)} returns true if:
055   *       <ul>
056   *         <li>{@code proxy} and {@code argument} are of the same type
057   *         <li>and {@link AbstractInvocationHandler#equals} returns true for the {@link
058   *             InvocationHandler} of {@code argument}
059   *       </ul>
060   *   <li>other method calls are dispatched to {@link #handleInvocation}.
061   * </ul>
062   */
063  @Override
064  @CheckForNull
065  public final Object invoke(Object proxy, Method method, @CheckForNull @Nullable Object[] args)
066      throws Throwable {
067    if (args == null) {
068      args = NO_ARGS;
069    }
070    if (args.length == 0 && method.getName().equals("hashCode")) {
071      return hashCode();
072    }
073    if (args.length == 1
074        && method.getName().equals("equals")
075        && method.getParameterTypes()[0] == Object.class) {
076      Object arg = args[0];
077      if (arg == null) {
078        return false;
079      }
080      if (proxy == arg) {
081        return true;
082      }
083      return isProxyOfSameInterfaces(arg, proxy.getClass())
084          && equals(Proxy.getInvocationHandler(arg));
085    }
086    if (args.length == 0 && method.getName().equals("toString")) {
087      return toString();
088    }
089    return handleInvocation(proxy, method, args);
090  }
091
092  /**
093   * {@link #invoke} delegates to this method upon any method invocation on the proxy instance,
094   * except {@link Object#equals}, {@link Object#hashCode} and {@link Object#toString}. The result
095   * will be returned as the proxied method's return value.
096   *
097   * <p>Unlike {@link #invoke}, {@code args} will never be null. When the method has no parameter,
098   * an empty array is passed in.
099   */
100  @CheckForNull
101  protected abstract Object handleInvocation(Object proxy, Method method, @Nullable Object[] args)
102      throws Throwable;
103
104  /**
105   * By default delegates to {@link Object#equals} so instances are only equal if they are
106   * identical. {@code proxy.equals(argument)} returns true if:
107   *
108   * <ul>
109   *   <li>{@code proxy} and {@code argument} are of the same type
110   *   <li>and this method returns true for the {@link InvocationHandler} of {@code argument}
111   * </ul>
112   *
113   * <p>Subclasses can override this method to provide custom equality.
114   */
115  @Override
116  public boolean equals(@CheckForNull Object obj) {
117    return super.equals(obj);
118  }
119
120  /**
121   * By default delegates to {@link Object#hashCode}. The dynamic proxies' {@code hashCode()} will
122   * delegate to this method. Subclasses can override this method to provide custom equality.
123   */
124  @Override
125  public int hashCode() {
126    return super.hashCode();
127  }
128
129  /**
130   * By default delegates to {@link Object#toString}. The dynamic proxies' {@code toString()} will
131   * delegate to this method. Subclasses can override this method to provide custom string
132   * representation for the proxies.
133   */
134  @Override
135  public String toString() {
136    return super.toString();
137  }
138
139  private static boolean isProxyOfSameInterfaces(Object arg, Class<?> proxyClass) {
140    return proxyClass.isInstance(arg)
141        // Equal proxy instances should mostly be instance of proxyClass
142        // Under some edge cases (such as the proxy of JDK types serialized and then deserialized)
143        // the proxy type may not be the same.
144        // We first check isProxyClass() so that the common case of comparing with non-proxy objects
145        // is efficient.
146        || (Proxy.isProxyClass(arg.getClass())
147            && Arrays.equals(arg.getClass().getInterfaces(), proxyClass.getInterfaces()));
148  }
149}