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, @Nullable Object[] args)76 protected @Nullable Object handleInvocation( 77 Object proxy, Method method, @Nullable Object[] args) { 78 Invokable<?, ?> invokable = interfaceType.method(method); 79 ImmutableList<Parameter> params = invokable.getParameters(); 80 for (int i = 0; i < args.length; i++) { 81 Parameter param = params.get(i); 82 if (!isNullable(param)) { 83 checkNotNull(args[i]); 84 } 85 } 86 return dummyReturnValue(interfaceType.resolveType(method.getGenericReturnType())); 87 } 88 89 @Override hashCode()90 public int hashCode() { 91 return identity().hashCode(); 92 } 93 94 @Override equals(@ullable Object obj)95 public boolean equals(@Nullable Object obj) { 96 if (obj instanceof DummyHandler) { 97 DummyHandler that = (DummyHandler) obj; 98 return identity().equals(that.identity()); 99 } else { 100 return false; 101 } 102 } 103 identity()104 private DummyProxy identity() { 105 return DummyProxy.this; 106 } 107 108 @Override toString()109 public String toString() { 110 return "Dummy proxy for " + interfaceType; 111 } 112 113 // Since type variables aren't serializable, reduce the type down to raw type before 114 // serialization. writeReplace()115 private Object writeReplace() { 116 return new DummyHandler(TypeToken.of(interfaceType.getRawType())); 117 } 118 } 119 } 120