<commit_before>/*
 * Grapht, an open source dependency injector.
 * Copyright 2014-2015 various contributors (see CONTRIBUTORS.txt)
 * Copyright 2010-2014 Regents of the University of Minnesota
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package org.grouplens.grapht;

import org.apache.commons.lang3.ClassUtils;
import org.grouplens.grapht.BindingFunctionBuilder.RuleSet;
import org.grouplens.grapht.annotation.DefaultImplementation;
import org.grouplens.grapht.annotation.DefaultProvider;
import org.grouplens.grapht.context.ContextMatcher;
import org.grouplens.grapht.reflect.*;
import org.grouplens.grapht.solver.BindRuleBuilder;
import org.grouplens.grapht.solver.BindingFlag;
import org.grouplens.grapht.util.Preconditions;
import org.grouplens.grapht.util.Types;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Provider;
import java.lang.annotation.Annotation;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * BindingImpl is the default implementation of Binding that is used by
 * {@link BindingFunctionBuilder}.
 * 
 * @author <a href="http://grouplens.org">GroupLens Research</a>
 * @param <T> The bindings source's type
 */
class BindingImpl<T> implements Binding<T> {
    private static final Logger logger = LoggerFactory.getLogger(BindingImpl.class);
    
    private final ContextImpl context;
    private final Class<T> sourceType;
    
    private final Set<Class<?>> excludeTypes;
    
    private final QualifierMatcher qualifier;

    private final CachePolicy cachePolicy;
    private final boolean fixed;

    public BindingImpl(ContextImpl context, Class<T> type) {
        this(context, type, context.getBuilder().getDefaultExclusions(),
             Qualifiers.matchDefault(),
             CachePolicy.NO_PREFERENCE,
             false);
    }

    public BindingImpl(ContextImpl context, Class<T> type,
                       Set<Class<?>> excludes, QualifierMatcher matcher, 
                       CachePolicy cachePolicy, boolean fixed) {
        this.context = context;
        this.cachePolicy = cachePolicy;
        sourceType = type;
        excludeTypes = excludes;
        qualifier = matcher;
        this.fixed = fixed;
    }

    @Override
    public Binding<T> withQualifier(@Nonnull Class<? extends Annotation> qualifier) {
        QualifierMatcher q = Qualifiers.match(qualifier);
        return new BindingImpl<T>(context, sourceType, excludeTypes, q, cachePolicy, fixed);
    }
    
    @Override
    public Binding<T> withQualifier(@Nonnull Annotation annot) {
        QualifierMatcher q = Qualifiers.match(annot);
        return new BindingImpl<T>(context, sourceType, excludeTypes, q, cachePolicy, fixed);
    }

    @Override
    public Binding<T> withAnyQualifier() {
        QualifierMatcher q = Qualifiers.matchAny();
        return new BindingImpl<T>(context, sourceType, excludeTypes, q, cachePolicy, fixed);
    }
    
    @Override
    public Binding<T> unqualified() {
        QualifierMatcher q = Qualifiers.matchNone();
        return new BindingImpl<T>(context, sourceType, excludeTypes, q, cachePolicy, fixed);
    }

    @Override
    public Binding<T> exclude(@Nonnull Class<?> exclude) {
        Preconditions.notNull("exclude type", exclude);
        Set<Class<?>> excludes = new HashSet<Class<?>>(excludeTypes);
        excludes.add(exclude);
        return new BindingImpl<T>(context, sourceType, excludes, qualifier, cachePolicy, fixed);
    }
    
    @Override
    public Binding<T> shared() {
        return new BindingImpl<T>(context, sourceType, excludeTypes, qualifier, CachePolicy.MEMOIZE, fixed);
    }
    
    @Override
    public Binding<T> unshared() {
        return new BindingImpl<T>(context, sourceType, excludeTypes, qualifier, CachePolicy.NEW_INSTANCE, fixed);
    }

    @Override
    public Binding<T> fixed() {
        return new BindingImpl<T>(context, sourceType, excludeTypes, qualifier, cachePolicy, true);
    }

    @Override
    public void to(@Nonnull Class<? extends T> impl, boolean chained) {
        Preconditions.isAssignable(sourceType, impl);
        if (logger.isWarnEnabled()) {
            if (Types.shouldBeInstantiable(impl)
                    && !Types.isInstantiable(impl)
                    && impl.getAnnotation(DefaultProvider.class) == null
                    && impl.getAnnotation(DefaultImplementation.class) == null) {
                logger.warn("Concrete type {} does not have an injectable or public default constructor, but probably should", impl);
            }
        }
        
        BindRuleBuilder brb = startRule();
        if (Types.isInstantiable(impl)) {
            brb.setSatisfaction(Satisfactions.type(impl));
        } else {
            brb.setImplementation(impl);
        }
        brb.setTerminal(!chained);
        generateBindings(brb, impl);
    }

    @Override
    public void to(@Nonnull Class<? extends T> impl) {
        to(impl, true);
    }

    @Override
    public void to(@Nullable T instance) {
        if (instance == null) {
            toNull();
            return;
        } else if (!(instance instanceof Number)
                   && !ClassUtils.isPrimitiveWrapper(instance.getClass())
                   && !sourceType.isInstance(instance)) {
            String msg = String.format("%s is not an instance of %s",
                                       instance, sourceType);
            throw new InvalidBindingException(sourceType, msg);
        }

        // Apply some type coercing if we're dealing with primitive types
        Object coerced = coerce(instance);
        Satisfaction s = Satisfactions.instance(coerced);
        BindRuleBuilder brb = startRule().setSatisfaction(s);
        generateBindings(brb, coerced.getClass());
    }
    
    @Override
    public void toProvider(@Nonnull Class<? extends Provider<? extends T>> provider) {
        Satisfaction s = Satisfactions.providerType(provider);
        BindRuleBuilder brb = startRule().setSatisfaction(s);
        generateBindings(brb, Types.getProvidedType(provider));
    }

    @Override
    public void toProvider(@Nonnull Provider<? extends T> provider) {
        Satisfaction s = Satisfactions.providerInstance(provider);
        BindRuleBuilder brb = startRule().setSatisfaction(s);<commit_msg>Implement generic provider fix in both binding methods<commit_after>/*
 * Grapht, an open source dependency injector.
 * Copyright 2014-2015 various contributors (see CONTRIBUTORS.txt)
 * Copyright 2010-2014 Regents of the University of Minnesota
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package org.grouplens.grapht;

import org.apache.commons.lang3.ClassUtils;
import org.grouplens.grapht.BindingFunctionBuilder.RuleSet;
import org.grouplens.grapht.annotation.DefaultImplementation;
import org.grouplens.grapht.annotation.DefaultProvider;
import org.grouplens.grapht.context.ContextMatcher;
import org.grouplens.grapht.reflect.*;
import org.grouplens.grapht.solver.BindRuleBuilder;
import org.grouplens.grapht.solver.BindingFlag;
import org.grouplens.grapht.util.Preconditions;
import org.grouplens.grapht.util.Types;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Provider;
import java.lang.annotation.Annotation;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * BindingImpl is the default implementation of Binding that is used by
 * {@link BindingFunctionBuilder}.
 * 
 * @author <a href="http://grouplens.org">GroupLens Research</a>
 * @param <T> The bindings source's type
 */
class BindingImpl<T> implements Binding<T> {
    private static final Logger logger = LoggerFactory.getLogger(BindingImpl.class);
    
    private final ContextImpl context;
    private final Class<T> sourceType;
    
    private final Set<Class<?>> excludeTypes;
    
    private final QualifierMatcher qualifier;

    private final CachePolicy cachePolicy;
    private final boolean fixed;

    public BindingImpl(ContextImpl context, Class<T> type) {
        this(context, type, context.getBuilder().getDefaultExclusions(),
             Qualifiers.matchDefault(),
             CachePolicy.NO_PREFERENCE,
             false);
    }

    public BindingImpl(ContextImpl context, Class<T> type,
                       Set<Class<?>> excludes, QualifierMatcher matcher, 
                       CachePolicy cachePolicy, boolean fixed) {
        this.context = context;
        this.cachePolicy = cachePolicy;
        sourceType = type;
        excludeTypes = excludes;
        qualifier = matcher;
        this.fixed = fixed;
    }

    @Override
    public Binding<T> withQualifier(@Nonnull Class<? extends Annotation> qualifier) {
        QualifierMatcher q = Qualifiers.match(qualifier);
        return new BindingImpl<T>(context, sourceType, excludeTypes, q, cachePolicy, fixed);
    }
    
    @Override
    public Binding<T> withQualifier(@Nonnull Annotation annot) {
        QualifierMatcher q = Qualifiers.match(annot);
        return new BindingImpl<T>(context, sourceType, excludeTypes, q, cachePolicy, fixed);
    }

    @Override
    public Binding<T> withAnyQualifier() {
        QualifierMatcher q = Qualifiers.matchAny();
        return new BindingImpl<T>(context, sourceType, excludeTypes, q, cachePolicy, fixed);
    }
    
    @Override
    public Binding<T> unqualified() {
        QualifierMatcher q = Qualifiers.matchNone();
        return new BindingImpl<T>(context, sourceType, excludeTypes, q, cachePolicy, fixed);
    }

    @Override
    public Binding<T> exclude(@Nonnull Class<?> exclude) {
        Preconditions.notNull("exclude type", exclude);
        Set<Class<?>> excludes = new HashSet<Class<?>>(excludeTypes);
        excludes.add(exclude);
        return new BindingImpl<T>(context, sourceType, excludes, qualifier, cachePolicy, fixed);
    }
    
    @Override
    public Binding<T> shared() {
        return new BindingImpl<T>(context, sourceType, excludeTypes, qualifier, CachePolicy.MEMOIZE, fixed);
    }
    
    @Override
    public Binding<T> unshared() {
        return new BindingImpl<T>(context, sourceType, excludeTypes, qualifier, CachePolicy.NEW_INSTANCE, fixed);
    }

    @Override
    public Binding<T> fixed() {
        return new BindingImpl<T>(context, sourceType, excludeTypes, qualifier, cachePolicy, true);
    }

    @Override
    public void to(@Nonnull Class<? extends T> impl, boolean chained) {
        Preconditions.isAssignable(sourceType, impl);
        if (logger.isWarnEnabled()) {
            if (Types.shouldBeInstantiable(impl)
                    && !Types.isInstantiable(impl)
                    && impl.getAnnotation(DefaultProvider.class) == null
                    && impl.getAnnotation(DefaultImplementation.class) == null) {
                logger.warn("Concrete type {} does not have an injectable or public default constructor, but probably should", impl);
            }
        }
        
        BindRuleBuilder brb = startRule();
        if (Types.isInstantiable(impl)) {
            brb.setSatisfaction(Satisfactions.type(impl));
        } else {
            brb.setImplementation(impl);
        }
        brb.setTerminal(!chained);
        generateBindings(brb, impl);
    }

    @Override
    public void to(@Nonnull Class<? extends T> impl) {
        to(impl, true);
    }

    @Override
    public void to(@Nullable T instance) {
        if (instance == null) {
            toNull();
            return;
        } else if (!(instance instanceof Number)
                   && !ClassUtils.isPrimitiveWrapper(instance.getClass())
                   && !sourceType.isInstance(instance)) {
            String msg = String.format("%s is not an instance of %s",
                                       instance, sourceType);
            throw new InvalidBindingException(sourceType, msg);
        }

        // Apply some type coercing if we're dealing with primitive types
        Object coerced = coerce(instance);
        Satisfaction s = Satisfactions.instance(coerced);
        BindRuleBuilder brb = startRule().setSatisfaction(s);
        generateBindings(brb, coerced.getClass());
    }
    
    @Override
    public void toProvider(@Nonnull Class<? extends Provider<? extends T>> provider) {
        Satisfaction s = Satisfactions.providerType(provider);
        BindRuleBuilder brb = startRule().setSatisfaction(s);
        Class<?> provided;
        try {
            provided = Types.getProvidedType(provider);
        } catch (IllegalArgumentException e) {
            if (e.getMessage().endsWith("is generic")) {
                throw new InvalidBindingException(sourceType, "cannot bind to generic provider");
            } else {
                throw e;
            }
        }
        generateBindings(brb, provided);
    }

    @Override
    public void toProvider(@Nonnull Provider<? extends T> provider) {
        Satisfaction s = Satisfactions.providerInstance(provider);
        BindRuleBuilder brb = startRule().setSatisfaction(s);
{'fraction_non_alphanumeric': 0.09591462171164435, 'fraction_numerical': 0.00435048603086126, 'mean_word_length': 3.01418826739427, 'pattern_counts': {'":': 0, '<': 82, '<?xml version=': 0, '>': 82, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4886990', 'n_tokens_mistral': 4098, 'n_tokens_neox': 3912, 'n_words': 1115}