Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2008 Google Inc.
      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.inject.util;
     18 
     19 import com.google.common.collect.ImmutableList;
     20 import com.google.common.collect.ImmutableSet;
     21 import com.google.common.collect.Iterables;
     22 import com.google.common.collect.Lists;
     23 import com.google.common.collect.Maps;
     24 import com.google.common.collect.Sets;
     25 import com.google.inject.AbstractModule;
     26 import com.google.inject.Binder;
     27 import com.google.inject.Binding;
     28 import com.google.inject.Key;
     29 import com.google.inject.Module;
     30 import com.google.inject.PrivateBinder;
     31 import com.google.inject.PrivateModule;
     32 import com.google.inject.Scope;
     33 import com.google.inject.internal.Errors;
     34 import com.google.inject.spi.DefaultBindingScopingVisitor;
     35 import com.google.inject.spi.DefaultElementVisitor;
     36 import com.google.inject.spi.Element;
     37 import com.google.inject.spi.ElementVisitor;
     38 import com.google.inject.spi.Elements;
     39 import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
     40 import com.google.inject.spi.PrivateElements;
     41 import com.google.inject.spi.ScopeBinding;
     42 import java.lang.annotation.Annotation;
     43 import java.util.Arrays;
     44 import java.util.LinkedHashSet;
     45 import java.util.List;
     46 import java.util.Map;
     47 import java.util.Set;
     48 
     49 /**
     50  * Static utility methods for creating and working with instances of {@link Module}.
     51  *
     52  * @author jessewilson (at) google.com (Jesse Wilson)
     53  * @since 2.0
     54  */
     55 public final class Modules {
     56   private Modules() {}
     57 
     58   public static final Module EMPTY_MODULE = new EmptyModule();
     59 
     60   private static class EmptyModule implements Module {
     61     @Override
     62     public void configure(Binder binder) {}
     63   }
     64 
     65   /**
     66    * Returns a builder that creates a module that overlays override modules over the given modules.
     67    * If a key is bound in both sets of modules, only the binding from the override modules is kept.
     68    * If a single {@link PrivateModule} is supplied or all elements are from a single {@link
     69    * PrivateBinder}, then this will overwrite the private bindings. Otherwise, private bindings will
     70    * not be overwritten unless they are exposed. This can be used to replace the bindings of a
     71    * production module with test bindings:
     72    *
     73    * <pre>
     74    * Module functionalTestModule
     75    *     = Modules.override(new ProductionModule()).with(new TestModule());
     76    * </pre>
     77    *
     78    * <p>Prefer to write smaller modules that can be reused and tested without overrides.
     79    *
     80    * @param modules the modules whose bindings are open to be overridden
     81    */
     82   public static OverriddenModuleBuilder override(Module... modules) {
     83     return new RealOverriddenModuleBuilder(Arrays.asList(modules));
     84   }
     85 
     86   /**
     87    * Returns a builder that creates a module that overlays override modules over the given modules.
     88    * If a key is bound in both sets of modules, only the binding from the override modules is kept.
     89    * If a single {@link PrivateModule} is supplied or all elements are from a single {@link
     90    * PrivateBinder}, then this will overwrite the private bindings. Otherwise, private bindings will
     91    * not be overwritten unless they are exposed. This can be used to replace the bindings of a
     92    * production module with test bindings:
     93    *
     94    * <pre>
     95    * Module functionalTestModule
     96    *     = Modules.override(getProductionModules()).with(getTestModules());
     97    * </pre>
     98    *
     99    * <p>Prefer to write smaller modules that can be reused and tested without overrides.
    100    *
    101    * @param modules the modules whose bindings are open to be overridden
    102    */
    103   public static OverriddenModuleBuilder override(Iterable<? extends Module> modules) {
    104     return new RealOverriddenModuleBuilder(modules);
    105   }
    106 
    107   /** Returns a new module that installs all of {@code modules}. */
    108   public static Module combine(Module... modules) {
    109     return combine(ImmutableSet.copyOf(modules));
    110   }
    111 
    112   /** Returns a new module that installs all of {@code modules}. */
    113   public static Module combine(Iterable<? extends Module> modules) {
    114     return new CombinedModule(modules);
    115   }
    116 
    117   private static class CombinedModule implements Module {
    118     final Set<Module> modulesSet;
    119 
    120     CombinedModule(Iterable<? extends Module> modules) {
    121       this.modulesSet = ImmutableSet.copyOf(modules);
    122     }
    123 
    124     @Override
    125     public void configure(Binder binder) {
    126       binder = binder.skipSources(getClass());
    127       for (Module module : modulesSet) {
    128         binder.install(module);
    129       }
    130     }
    131   }
    132 
    133   /** See the EDSL example at {@link Modules#override(Module[]) override()}. */
    134   public interface OverriddenModuleBuilder {
    135 
    136     /** See the EDSL example at {@link Modules#override(Module[]) override()}. */
    137     Module with(Module... overrides);
    138 
    139     /** See the EDSL example at {@link Modules#override(Module[]) override()}. */
    140     Module with(Iterable<? extends Module> overrides);
    141   }
    142 
    143   private static final class RealOverriddenModuleBuilder implements OverriddenModuleBuilder {
    144     private final ImmutableSet<Module> baseModules;
    145 
    146     private RealOverriddenModuleBuilder(Iterable<? extends Module> baseModules) {
    147       this.baseModules = ImmutableSet.copyOf(baseModules);
    148     }
    149 
    150     @Override
    151     public Module with(Module... overrides) {
    152       return with(Arrays.asList(overrides));
    153     }
    154 
    155     @Override
    156     public Module with(Iterable<? extends Module> overrides) {
    157       return new OverrideModule(overrides, baseModules);
    158     }
    159   }
    160 
    161   static class OverrideModule extends AbstractModule {
    162     private final ImmutableSet<Module> overrides;
    163     private final ImmutableSet<Module> baseModules;
    164 
    165     OverrideModule(Iterable<? extends Module> overrides, ImmutableSet<Module> baseModules) {
    166       this.overrides = ImmutableSet.copyOf(overrides);
    167       this.baseModules = baseModules;
    168     }
    169 
    170     @Override
    171     public void configure() {
    172       Binder baseBinder = binder();
    173       List<Element> baseElements = Elements.getElements(currentStage(), baseModules);
    174 
    175       // If the sole element was a PrivateElements, we want to override
    176       // the private elements within that -- so refocus our elements
    177       // and binder.
    178       if (baseElements.size() == 1) {
    179         Element element = Iterables.getOnlyElement(baseElements);
    180         if (element instanceof PrivateElements) {
    181           PrivateElements privateElements = (PrivateElements) element;
    182           PrivateBinder privateBinder =
    183               baseBinder.newPrivateBinder().withSource(privateElements.getSource());
    184           for (Key exposed : privateElements.getExposedKeys()) {
    185             privateBinder.withSource(privateElements.getExposedSource(exposed)).expose(exposed);
    186           }
    187           baseBinder = privateBinder;
    188           baseElements = privateElements.getElements();
    189         }
    190       }
    191 
    192       final Binder binder = baseBinder.skipSources(this.getClass());
    193       final LinkedHashSet<Element> elements = new LinkedHashSet<>(baseElements);
    194       final Module scannersModule = extractScanners(elements);
    195       final List<Element> overrideElements =
    196           Elements.getElements(
    197               currentStage(),
    198               ImmutableList.<Module>builder().addAll(overrides).add(scannersModule).build());
    199 
    200       final Set<Key<?>> overriddenKeys = Sets.newHashSet();
    201       final Map<Class<? extends Annotation>, ScopeBinding> overridesScopeAnnotations =
    202           Maps.newHashMap();
    203 
    204       // execute the overrides module, keeping track of which keys and scopes are bound
    205       new ModuleWriter(binder) {
    206         @Override
    207         public <T> Void visit(Binding<T> binding) {
    208           overriddenKeys.add(binding.getKey());
    209           return super.visit(binding);
    210         }
    211 
    212         @Override
    213         public Void visit(ScopeBinding scopeBinding) {
    214           overridesScopeAnnotations.put(scopeBinding.getAnnotationType(), scopeBinding);
    215           return super.visit(scopeBinding);
    216         }
    217 
    218         @Override
    219         public Void visit(PrivateElements privateElements) {
    220           overriddenKeys.addAll(privateElements.getExposedKeys());
    221           return super.visit(privateElements);
    222         }
    223       }.writeAll(overrideElements);
    224 
    225       // execute the original module, skipping all scopes and overridden keys. We only skip each
    226       // overridden binding once so things still blow up if the module binds the same thing
    227       // multiple times.
    228       final Map<Scope, List<Object>> scopeInstancesInUse = Maps.newHashMap();
    229       final List<ScopeBinding> scopeBindings = Lists.newArrayList();
    230       new ModuleWriter(binder) {
    231         @Override
    232         public <T> Void visit(Binding<T> binding) {
    233           if (!overriddenKeys.remove(binding.getKey())) {
    234             super.visit(binding);
    235 
    236             // Record when a scope instance is used in a binding
    237             Scope scope = getScopeInstanceOrNull(binding);
    238             if (scope != null) {
    239               List<Object> existing = scopeInstancesInUse.get(scope);
    240               if (existing == null) {
    241                 existing = Lists.newArrayList();
    242                 scopeInstancesInUse.put(scope, existing);
    243               }
    244               existing.add(binding.getSource());
    245             }
    246           }
    247 
    248           return null;
    249         }
    250 
    251         void rewrite(Binder binder, PrivateElements privateElements, Set<Key<?>> keysToSkip) {
    252           PrivateBinder privateBinder =
    253               binder.withSource(privateElements.getSource()).newPrivateBinder();
    254 
    255           Set<Key<?>> skippedExposes = Sets.newHashSet();
    256 
    257           for (Key<?> key : privateElements.getExposedKeys()) {
    258             if (keysToSkip.remove(key)) {
    259               skippedExposes.add(key);
    260             } else {
    261               privateBinder.withSource(privateElements.getExposedSource(key)).expose(key);
    262             }
    263           }
    264 
    265           for (Element element : privateElements.getElements()) {
    266             if (element instanceof Binding && skippedExposes.remove(((Binding) element).getKey())) {
    267               continue;
    268             }
    269             if (element instanceof PrivateElements) {
    270               rewrite(privateBinder, (PrivateElements) element, skippedExposes);
    271               continue;
    272             }
    273             element.applyTo(privateBinder);
    274           }
    275         }
    276 
    277         @Override
    278         public Void visit(PrivateElements privateElements) {
    279           rewrite(binder, privateElements, overriddenKeys);
    280           return null;
    281         }
    282 
    283         @Override
    284         public Void visit(ScopeBinding scopeBinding) {
    285           scopeBindings.add(scopeBinding);
    286           return null;
    287         }
    288       }.writeAll(elements);
    289 
    290       // execute the scope bindings, skipping scopes that have been overridden. Any scope that
    291       // is overridden and in active use will prompt an error
    292       new ModuleWriter(binder) {
    293         @Override
    294         public Void visit(ScopeBinding scopeBinding) {
    295           ScopeBinding overideBinding =
    296               overridesScopeAnnotations.remove(scopeBinding.getAnnotationType());
    297           if (overideBinding == null) {
    298             super.visit(scopeBinding);
    299           } else {
    300             List<Object> usedSources = scopeInstancesInUse.get(scopeBinding.getScope());
    301             if (usedSources != null) {
    302               StringBuilder sb =
    303                   new StringBuilder(
    304                       "The scope for @%s is bound directly and cannot be overridden.");
    305               sb.append("%n     original binding at " + Errors.convert(scopeBinding.getSource()));
    306               for (Object usedSource : usedSources) {
    307                 sb.append("%n     bound directly at " + Errors.convert(usedSource) + "");
    308               }
    309               binder
    310                   .withSource(overideBinding.getSource())
    311                   .addError(sb.toString(), scopeBinding.getAnnotationType().getSimpleName());
    312             }
    313           }
    314           return null;
    315         }
    316       }.writeAll(scopeBindings);
    317     }
    318 
    319     private Scope getScopeInstanceOrNull(Binding<?> binding) {
    320       return binding.acceptScopingVisitor(
    321           new DefaultBindingScopingVisitor<Scope>() {
    322             @Override
    323             public Scope visitScope(Scope scope) {
    324               return scope;
    325             }
    326           });
    327     }
    328   }
    329 
    330   private static class ModuleWriter extends DefaultElementVisitor<Void> {
    331     protected final Binder binder;
    332 
    333     ModuleWriter(Binder binder) {
    334       this.binder = binder.skipSources(this.getClass());
    335     }
    336 
    337     @Override
    338     protected Void visitOther(Element element) {
    339       element.applyTo(binder);
    340       return null;
    341     }
    342 
    343     void writeAll(Iterable<? extends Element> elements) {
    344       for (Element element : elements) {
    345         element.acceptVisitor(this);
    346       }
    347     }
    348   }
    349 
    350   private static Module extractScanners(Iterable<Element> elements) {
    351     final List<ModuleAnnotatedMethodScannerBinding> scanners = Lists.newArrayList();
    352     ElementVisitor<Void> visitor =
    353         new DefaultElementVisitor<Void>() {
    354           @Override
    355           public Void visit(ModuleAnnotatedMethodScannerBinding binding) {
    356             scanners.add(binding);
    357             return null;
    358           }
    359         };
    360     for (Element element : elements) {
    361       element.acceptVisitor(visitor);
    362     }
    363     return new AbstractModule() {
    364       @Override
    365       protected void configure() {
    366         for (ModuleAnnotatedMethodScannerBinding scanner : scanners) {
    367           scanner.applyTo(binder());
    368         }
    369       }
    370     };
    371   }
    372 }
    373