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 import static com.google.common.testing.NullPointerTester.isNullable; 21 22 import com.google.common.annotations.GwtIncompatible; 23 import com.google.common.collect.ImmutableList; 24 import com.google.common.collect.Sets; 25 import com.google.common.reflect.AbstractInvocationHandler; 26 import com.google.common.reflect.Invokable; 27 import com.google.common.reflect.Parameter; 28 import com.google.common.reflect.TypeToken; 29 import java.io.Serializable; 30 import java.lang.reflect.Method; 31 import java.lang.reflect.Proxy; 32 import java.util.Set; 33 34 /** 35 * Generates a dummy interface proxy that simply returns a dummy value for each method. 36 * 37 * @author Ben Yu 38 */ 39 @GwtIncompatible 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 = 52 Proxy.newProxyInstance( 53 interfaceClasses.iterator().next().getClassLoader(), 54 interfaceClasses.toArray(new Class<?>[interfaceClasses.size()]), 55 new DummyHandler(interfaceType)); 56 @SuppressWarnings("unchecked") // interfaceType is T 57 T result = (T) dummy; 58 return result; 59 } 60 61 /** Returns the dummy return value for {@code returnType}. */ dummyReturnValue(TypeToken<R> returnType)62 abstract <R> R dummyReturnValue(TypeToken<R> returnType); 63 64 private class DummyHandler extends AbstractInvocationHandler implements Serializable { 65 private final TypeToken<?> interfaceType; 66 DummyHandler(TypeToken<?> interfaceType)67 DummyHandler(TypeToken<?> interfaceType) { 68 this.interfaceType = interfaceType; 69 } 70 71 @Override handleInvocation(Object proxy, Method method, Object[] args)72 protected Object handleInvocation(Object proxy, Method method, Object[] args) { 73 Invokable<?, ?> invokable = interfaceType.method(method); 74 ImmutableList<Parameter> params = invokable.getParameters(); 75 for (int i = 0; i < args.length; i++) { 76 Parameter param = params.get(i); 77 if (!isNullable(param)) { 78 checkNotNull(args[i]); 79 } 80 } 81 return dummyReturnValue(interfaceType.resolveType(method.getGenericReturnType())); 82 } 83 84 @Override hashCode()85 public int hashCode() { 86 return identity().hashCode(); 87 } 88 89 @Override equals(Object obj)90 public boolean equals(Object obj) { 91 if (obj instanceof DummyHandler) { 92 DummyHandler that = (DummyHandler) obj; 93 return identity().equals(that.identity()); 94 } else { 95 return false; 96 } 97 } 98 identity()99 private DummyProxy identity() { 100 return DummyProxy.this; 101 } 102 103 @Override toString()104 public String toString() { 105 return "Dummy proxy for " + interfaceType; 106 } 107 108 // Since type variables aren't serializable, reduce the type down to raw type before 109 // serialization. writeReplace()110 private Object writeReplace() { 111 return new DummyHandler(TypeToken.of(interfaceType.getRawType())); 112 } 113 } 114 } 115