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