DummyProxy.java revision 7dd252788645e940eada959bdde927426e2531c9
1/*
2 * Copyright (C) 2012 The Guava Authors
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
17package com.google.common.testing;
18
19import static com.google.common.base.Preconditions.checkNotNull;
20
21import com.google.common.collect.ImmutableList;
22import com.google.common.collect.Sets;
23import com.google.common.reflect.AbstractInvocationHandler;
24import com.google.common.reflect.Invokable;
25import com.google.common.reflect.Parameter;
26import com.google.common.reflect.TypeToken;
27
28import java.io.Serializable;
29import java.lang.reflect.Method;
30import java.lang.reflect.Proxy;
31import java.util.Set;
32
33import javax.annotation.Nullable;
34
35/**
36 * Generates a dummy interface proxy that simply returns a dummy value for each method.
37 *
38 * @author Ben Yu
39 */
40abstract class DummyProxy {
41
42  /**
43   * Returns a new proxy for {@code interfaceType}. Proxies of the same interface are equal to each
44   * other if the {@link DummyProxy} instance that created the proxies are equal.
45   */
46  final <T> T newProxy(TypeToken<T> interfaceType) {
47    Set<Class<?>> interfaceClasses = Sets.newLinkedHashSet();
48    interfaceClasses.addAll(interfaceType.getTypes().interfaces().rawTypes());
49    // Make the proxy serializable to work with SerializableTester
50    interfaceClasses.add(Serializable.class);
51    Object dummy = Proxy.newProxyInstance(
52        interfaceClasses.iterator().next().getClassLoader(),
53        interfaceClasses.toArray(new Class<?>[interfaceClasses.size()]),
54        new DummyHandler(interfaceType));
55    @SuppressWarnings("unchecked") // interfaceType is T
56    T result = (T) dummy;
57    return result;
58  }
59
60  /** Returns the dummy return value for {@code returnType}. */
61  abstract <R> R dummyReturnValue(TypeToken<R> returnType);
62
63  private class DummyHandler extends AbstractInvocationHandler implements Serializable {
64    private final TypeToken<?> interfaceType;
65
66    DummyHandler(TypeToken<?> interfaceType) {
67      this.interfaceType = interfaceType;
68    }
69
70    @Override protected Object handleInvocation(
71        Object proxy, Method method, Object[] args) {
72      Invokable<?, ?> invokable = interfaceType.method(method);
73      ImmutableList<Parameter> params = invokable.getParameters();
74      for (int i = 0; i < args.length; i++) {
75        Parameter param = params.get(i);
76        if (!param.isAnnotationPresent(Nullable.class)) {
77          checkNotNull(args[i]);
78        }
79      }
80      return dummyReturnValue(interfaceType.resolveType(method.getGenericReturnType()));
81    }
82
83    @Override public int hashCode() {
84      return identity().hashCode();
85    }
86
87    @Override public boolean equals(Object obj) {
88      if (obj instanceof DummyHandler) {
89        DummyHandler that = (DummyHandler) obj;
90        return identity().equals(that.identity());
91      } else {
92        return false;
93      }
94    }
95
96    private DummyProxy identity() {
97      return DummyProxy.this;
98    }
99
100    @Override public String toString() {
101      return "Dummy proxy for " + interfaceType;
102    }
103
104    // Since type variables aren't serializable, reduce the type down to raw type before
105    // serialization.
106    private Object writeReplace() {
107      return new DummyHandler(TypeToken.of(interfaceType.getRawType()));
108    }
109  }
110}
111