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 
17 package com.google.common.testing;
18 
19 import static com.google.common.base.Preconditions.checkNotNull;
20 
21 import com.google.common.collect.ImmutableList;
22 import com.google.common.collect.Sets;
23 import com.google.common.reflect.AbstractInvocationHandler;
24 import com.google.common.reflect.Invokable;
25 import com.google.common.reflect.Parameter;
26 import com.google.common.reflect.TypeToken;
27 
28 import java.io.Serializable;
29 import java.lang.reflect.Method;
30 import java.lang.reflect.Proxy;
31 import java.util.Set;
32 
33 import 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  */
40 abstract 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    */
newProxy(TypeToken<T> interfaceType)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}. */
dummyReturnValue(TypeToken<R> returnType)61   abstract <R> R dummyReturnValue(TypeToken<R> returnType);
62 
63   private class DummyHandler extends AbstractInvocationHandler implements Serializable {
64     private final TypeToken<?> interfaceType;
65 
DummyHandler(TypeToken<?> interfaceType)66     DummyHandler(TypeToken<?> interfaceType) {
67       this.interfaceType = interfaceType;
68     }
69 
handleInvocation( Object proxy, Method method, Object[] args)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 
hashCode()83     @Override public int hashCode() {
84       return identity().hashCode();
85     }
86 
equals(Object obj)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 
identity()96     private DummyProxy identity() {
97       return DummyProxy.this;
98     }
99 
toString()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.
writeReplace()106     private Object writeReplace() {
107       return new DummyHandler(TypeToken.of(interfaceType.getRawType()));
108     }
109   }
110 }
111