All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.inject.internal.BindingProcessor Maven / Gradle / Ivy

There is a newer version: 7.0.0
Show newest version
/**
 * Copyright (C) 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.inject.internal;

import com.google.inject.AbstractModule;
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.MembersInjector;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.Scope;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.ImmutableSet;
import com.google.inject.internal.util.Lists;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.ConstructorBinding;
import com.google.inject.spi.ConvertedConstantBinding;
import com.google.inject.spi.ExposedBinding;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.InstanceBinding;
import com.google.inject.spi.LinkedKeyBinding;
import com.google.inject.spi.PrivateElements;
import com.google.inject.spi.ProviderBinding;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderKeyBinding;
import com.google.inject.spi.UntargettedBinding;
import java.util.List;
import java.util.Set;

/**
 * Handles {@link Binder#bind} and {@link Binder#bindConstant} elements.
 *
 * @author [email protected] (Bob Lee)
 * @author [email protected] (Jesse Wilson)
 */
final class BindingProcessor extends AbstractProcessor {

  private final List creationListeners = Lists.newArrayList();
  private final Initializer initializer;
  private final List uninitializedBindings = Lists.newArrayList();

  BindingProcessor(Errors errors, Initializer initializer) {
    super(errors);
    this.initializer = initializer;
  }

  @Override public  Boolean visit(Binding command) {
    final Object source = command.getSource();

    if (Void.class.equals(command.getKey().getTypeLiteral().getRawType())) {
      if (command instanceof ProviderInstanceBinding
          && ((ProviderInstanceBinding) command).getProviderInstance() instanceof ProviderMethod) {
        errors.voidProviderMethod();
      } else {
        errors.missingConstantValues();
      }
      return true;
    }

    final Key key = command.getKey();
    Class rawType = key.getTypeLiteral().getRawType();

    if (rawType == Provider.class) {
      errors.bindingToProvider();
      return true;
    }

    validateKey(command.getSource(), command.getKey());

    final Scoping scoping = Scoping.makeInjectable(
        ((BindingImpl) command).getScoping(), injector, errors);

    command.acceptTargetVisitor(new BindingTargetVisitor() {
      public Void visit(ConstructorBinding binding) {
        try {
          ConstructorBindingImpl onInjector = ConstructorBindingImpl.create(injector, key, 
              binding.getConstructor(), source, scoping, errors, false);
          scheduleInitialization(onInjector);
          putBinding(onInjector);
        } catch (ErrorsException e) {
          errors.merge(e.getErrors());
          putBinding(invalidBinding(injector, key, source));
        }
        return null;
      }

      public Void visit(InstanceBinding binding) {
        Set injectionPoints = binding.getInjectionPoints();
        T instance = binding.getInstance();
        Initializable ref = initializer.requestInjection(
            injector, instance, source, injectionPoints);
        ConstantFactory factory = new ConstantFactory(ref);
        InternalFactory scopedFactory
            = Scoping.scope(key, injector, factory, source, scoping);
        putBinding(new InstanceBindingImpl(injector, key, source, scopedFactory, injectionPoints,
            instance));
        return null;
      }

      public Void visit(ProviderInstanceBinding binding) {
        Provider provider = binding.getProviderInstance();
        Set injectionPoints = binding.getInjectionPoints();
        Initializable> initializable = initializer
            .>requestInjection(injector, provider, source, injectionPoints);
        InternalFactory factory = new InternalFactoryToProviderAdapter(initializable, source);
        InternalFactory scopedFactory
            = Scoping.scope(key, injector, factory, source, scoping);
        putBinding(new ProviderInstanceBindingImpl(injector, key, source, scopedFactory, scoping,
            provider, injectionPoints));
        return null;
      }

      public Void visit(ProviderKeyBinding binding) {
        Key> providerKey = binding.getProviderKey();
        BoundProviderFactory boundProviderFactory
            = new BoundProviderFactory(injector, providerKey, source);
        creationListeners.add(boundProviderFactory);
        InternalFactory scopedFactory = Scoping.scope(
            key, injector, (InternalFactory) boundProviderFactory, source, scoping);
        putBinding(new LinkedProviderBindingImpl(
            injector, key, source, scopedFactory, scoping, providerKey));
        return null;
      }

      public Void visit(LinkedKeyBinding binding) {
        Key linkedKey = binding.getLinkedKey();
        if (key.equals(linkedKey)) {
          errors.recursiveBinding();
        }

        FactoryProxy factory = new FactoryProxy(injector, key, linkedKey, source);
        creationListeners.add(factory);
        InternalFactory scopedFactory
            = Scoping.scope(key, injector, factory, source, scoping);
        putBinding(
            new LinkedBindingImpl(injector, key, source, scopedFactory, scoping, linkedKey));
        return null;
      }

      public Void visit(UntargettedBinding untargetted) {
        // Error: Missing implementation.
        // Example: bind(Date.class).annotatedWith(Red.class);
        // We can't assume abstract types aren't injectable. They may have an
        // @ImplementedBy annotation or something.
        if (key.getAnnotationType() != null) {
          errors.missingImplementation(key);
          putBinding(invalidBinding(injector, key, source));
          return null;
        }

        // This cast is safe after the preceeding check.
        try {
          BindingImpl binding = injector.createUninitializedBinding(
              key, scoping, source, errors, false);
          scheduleInitialization(binding);
          putBinding(binding);
        } catch (ErrorsException e) {
          errors.merge(e.getErrors());
          putBinding(invalidBinding(injector, key, source));
        }

        return null;
      }

      public Void visit(ExposedBinding binding) {
        throw new IllegalArgumentException("Cannot apply a non-module element");
      }

      public Void visit(ConvertedConstantBinding binding) {
        throw new IllegalArgumentException("Cannot apply a non-module element");
      }

      public Void visit(ProviderBinding binding) {
        throw new IllegalArgumentException("Cannot apply a non-module element");
      }

      private void scheduleInitialization(final BindingImpl binding) {
        uninitializedBindings.add(new Runnable() {
          public void run() {
            try {
              binding.getInjector().initializeBinding(binding, errors.withSource(source));
            } catch (ErrorsException e) {
              errors.merge(e.getErrors());
            }
          }
        });
      }
    });

    return true;
  }

  @Override public Boolean visit(PrivateElements privateElements) {
    for (Key key : privateElements.getExposedKeys()) {
      bindExposed(privateElements, key);
    }
    return false; // leave the private elements for the PrivateElementsProcessor to handle
  }

  private  void bindExposed(PrivateElements privateElements, Key key) {
    ExposedKeyFactory exposedKeyFactory = new ExposedKeyFactory(key, privateElements);
    creationListeners.add(exposedKeyFactory);
    putBinding(new ExposedBindingImpl(
        injector, privateElements.getExposedSource(key), key, exposedKeyFactory, privateElements));
  }

  private  void validateKey(Object source, Key key) {
    Annotations.checkForMisplacedScopeAnnotations(
        key.getTypeLiteral().getRawType(), source, errors);
  }

   UntargettedBindingImpl invalidBinding(InjectorImpl injector, Key key, Object source) {
    return new UntargettedBindingImpl(injector, key, source);
  }

  public void initializeBindings() {
    for (Runnable initializer : uninitializedBindings) {
      initializer.run();
    }
  }

  public void runCreationListeners() {
    for (CreationListener creationListener : creationListeners) {
      creationListener.notify(errors);
    }
  }

  private void putBinding(BindingImpl binding) {
    Key key = binding.getKey();

    Class rawType = key.getTypeLiteral().getRawType();
    if (FORBIDDEN_TYPES.contains(rawType)) {
      errors.cannotBindToGuiceType(rawType.getSimpleName());
      return;
    }

    BindingImpl original = injector.getExistingBinding(key);
    if (original != null) {
      // If it failed because of an explicit duplicate binding...
      if (injector.state.getExplicitBinding(key) != null) {
        try {
          if(!isOkayDuplicate(original, binding, injector.state)) {
            errors.bindingAlreadySet(key, original.getSource());
            return;
          }
        } catch(Throwable t) {
          errors.errorCheckingDuplicateBinding(key, original.getSource(), t);
          return;
        }
      } else {
        // Otherwise, it failed because of a duplicate JIT binding
        // in the parent
        errors.jitBindingAlreadySet(key);
        return;
      }
    }

    // prevent the parent from creating a JIT binding for this key
    injector.state.parent().blacklist(key, binding.getSource());
    injector.state.putBinding(key, binding);
  }

  /**
   * We tolerate duplicate bindings if one exposes the other or if the two bindings
   * are considered duplicates (see {@link Bindings#areDuplicates(BindingImpl, BindingImpl)}.
   *
   * @param original the binding in the parent injector (candidate for an exposing binding)
   * @param binding the binding to check (candidate for the exposed binding)
   */
  private boolean isOkayDuplicate(BindingImpl original, BindingImpl binding, State state) {
    if (original instanceof ExposedBindingImpl) {
      ExposedBindingImpl exposed = (ExposedBindingImpl) original;
      InjectorImpl exposedFrom = (InjectorImpl) exposed.getPrivateElements().getInjector();
      return (exposedFrom == binding.getInjector());
    } else {
      original = (BindingImpl)state.getExplicitBindingsThisLevel().get(binding.getKey());
      // If no original at this level, the original was on a parent, and we don't
      // allow deduplication between parents & children.
      if(original == null) {
        return false;
      } else {
        return original.equals(binding);
      }
    }
  }

  // It's unfortunate that we have to maintain a blacklist of specific
  // classes, but we can't easily block the whole package because of
  // all our unit tests.
  private static final Set> FORBIDDEN_TYPES = ImmutableSet.>of(
      AbstractModule.class,
      Binder.class,
      Binding.class,
      Injector.class,
      Key.class,
      MembersInjector.class,
      Module.class,
      Provider.class,
      Scope.class,
      TypeLiteral.class);
  // TODO(jessewilson): fix BuiltInModule, then add Stage

  interface CreationListener {
    void notify(Errors errors);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy