Home | History | Annotate | Download | only in testing
      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    */
     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}. */
     61   abstract <R> R dummyReturnValue(TypeToken<R> returnType);
     62 
     63   private class DummyHandler extends AbstractInvocationHandler implements Serializable {
     64     private final TypeToken<?> interfaceType;
     65 
     66     DummyHandler(TypeToken<?> interfaceType) {
     67       this.interfaceType = interfaceType;
     68     }
     69 
     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 
     83     @Override public int hashCode() {
     84       return identity().hashCode();
     85     }
     86 
     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 
     96     private DummyProxy identity() {
     97       return DummyProxy.this;
     98     }
     99 
    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.
    106     private Object writeReplace() {
    107       return new DummyHandler(TypeToken.of(interfaceType.getRawType()));
    108     }
    109   }
    110 }
    111