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