Home | History | Annotate | Download | only in reflect
      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.reflect;
     18 
     19 import com.google.common.annotations.Beta;
     20 import com.google.common.collect.ForwardingMap;
     21 import com.google.common.collect.ImmutableMap;
     22 
     23 import java.util.Map;
     24 
     25 /**
     26  * A type-to-instance map backed by an {@link ImmutableMap}. See also {@link
     27  * MutableTypeToInstanceMap}.
     28  *
     29  * @author Ben Yu
     30  * @since 13.0
     31  */
     32 @Beta
     33 public final class ImmutableTypeToInstanceMap<B> extends ForwardingMap<TypeToken<? extends B>, B>
     34     implements TypeToInstanceMap<B> {
     35 
     36   /** Returns an empty type to instance map. */
     37   public static <B> ImmutableTypeToInstanceMap<B> of() {
     38     return new ImmutableTypeToInstanceMap<B>(ImmutableMap.<TypeToken<? extends B>, B>of());
     39   }
     40 
     41   /** Returns a new builder. */
     42   public static <B> Builder<B> builder() {
     43     return new Builder<B>();
     44   }
     45 
     46   /**
     47    * A builder for creating immutable type-to-instance maps. Example:
     48    * <pre>   {@code
     49    *
     50    *   static final ImmutableTypeToInstanceMap<Handler<?>> HANDLERS =
     51    *       ImmutableTypeToInstanceMap.<Handler<?>>builder()
     52    *           .put(new TypeToken<Handler<Foo>>() {}, new FooHandler())
     53    *           .put(new TypeToken<Handler<Bar>>() {}, new SubBarHandler())
     54    *           .build();}</pre>
     55    *
     56    * <p>After invoking {@link #build()} it is still possible to add more entries
     57    * and build again. Thus each map generated by this builder will be a superset
     58    * of any map generated before it.
     59    *
     60    * @since 13.0
     61    */
     62   @Beta
     63   public static final class Builder<B> {
     64     private final ImmutableMap.Builder<TypeToken<? extends B>, B> mapBuilder
     65         = ImmutableMap.builder();
     66 
     67     private Builder() {}
     68 
     69     /**
     70      * Associates {@code key} with {@code value} in the built map. Duplicate
     71      * keys are not allowed, and will cause {@link #build} to fail.
     72      */
     73     public <T extends B> Builder<B> put(Class<T> key, T value) {
     74       mapBuilder.put(TypeToken.of(key), value);
     75       return this;
     76     }
     77 
     78     /**
     79      * Associates {@code key} with {@code value} in the built map. Duplicate
     80      * keys are not allowed, and will cause {@link #build} to fail.
     81      */
     82     public <T extends B> Builder<B> put(TypeToken<T> key, T value) {
     83       mapBuilder.put(key.rejectTypeVariables(), value);
     84       return this;
     85     }
     86 
     87     /**
     88      * Returns a new immutable type-to-instance map containing the entries
     89      * provided to this builder.
     90      *
     91      * @throws IllegalArgumentException if duplicate keys were added
     92      */
     93     public ImmutableTypeToInstanceMap<B> build() {
     94       return new ImmutableTypeToInstanceMap<B>(mapBuilder.build());
     95     }
     96   }
     97 
     98   private final ImmutableMap<TypeToken<? extends B>, B> delegate;
     99 
    100   private ImmutableTypeToInstanceMap(ImmutableMap<TypeToken<? extends B>, B> delegate) {
    101     this.delegate = delegate;
    102   }
    103 
    104   @Override public <T extends B> T getInstance(TypeToken<T> type) {
    105     return trustedGet(type.rejectTypeVariables());
    106   }
    107 
    108   /**
    109    * Guaranteed to throw an exception and leave the map unmodified.
    110    *
    111    * @throws UnsupportedOperationException always
    112    */
    113   @Override public <T extends B> T putInstance(TypeToken<T> type, T value) {
    114     throw new UnsupportedOperationException();
    115   }
    116 
    117   @Override public <T extends B> T getInstance(Class<T> type) {
    118     return trustedGet(TypeToken.of(type));
    119   }
    120 
    121   /**
    122    * Guaranteed to throw an exception and leave the map unmodified.
    123    *
    124    * @throws UnsupportedOperationException always
    125    */
    126   @Override public <T extends B> T putInstance(Class<T> type, T value) {
    127     throw new UnsupportedOperationException();
    128   }
    129 
    130   @Override protected Map<TypeToken<? extends B>, B> delegate() {
    131     return delegate;
    132   }
    133 
    134   @SuppressWarnings("unchecked") // value could not get in if not a T
    135   private <T extends B> T trustedGet(TypeToken<T> type) {
    136     return (T) delegate.get(type);
    137   }
    138 }
    139