org.grouplens.grapht.BindingFunctionBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.grouplens.grapht.BindingFunctionBuilder.java

Source

/*
 * 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 com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import org.grouplens.grapht.context.ContextMatcher;
import org.grouplens.grapht.solver.BindRule;
import org.grouplens.grapht.solver.BindingFunction;
import org.grouplens.grapht.solver.RuleBasedBindingFunction;

import java.io.Externalizable;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * BindingFunctionBuilder provides a convenient access to the fluent API and
 * converts calls to {@link Context} and {@link Binding} methods into multiple
 * {@link BindingFunction BindingFunctions}.
 * 
 * @author <a href="http://grouplens.org">GroupLens Research</a>
 */
public class BindingFunctionBuilder implements Cloneable {
    /**
     * BindingFunctionBuilder generates three binding functions at separate
     * priorities.
     */
    public static enum RuleSet {
        /**
         * Rule set for the explicitly configured rules with the fluent API.
         */
        EXPLICIT,
        /**
         * Rule set for the intermediate types between a source type (
         * {@link Context#bind(Class)}) and the target type (
         * {@link Binding#to(Class)})
         */
        INTERMEDIATE_TYPES,
        /**
         * Rule set for the super types of the source types of bindings (e.g.
         * {@link Context#bind(Class)})
         */
        SUPER_TYPES
    }

    private final Context root;

    private final Set<Class<?>> defaultExcludes;
    private final boolean generateRules;

    private final Multimap<ContextMatcher, BindRule> manualRules;
    private final Multimap<ContextMatcher, BindRule> intermediateRules; // "generated"
    private final Multimap<ContextMatcher, BindRule> superRules; // "generated"

    /**
     * Create a new InjectorConfigurationBuilder that automatically generates bind rules for
     * super and intermediate types.
     */
    public BindingFunctionBuilder() {
        this(true);
    }

    /**
     * Create a new InjectorConfigurationBuilder. If <tt>generateRules</tt> is true, bind
     * rules for super and intermediate types are generated. If it is false,
     * only one bind rule is created per binding.
     * 
     * @param generateRules True if additional bind rules should be generated
     */
    public BindingFunctionBuilder(boolean generateRules) {
        this.generateRules = generateRules;

        defaultExcludes = new HashSet<Class<?>>();
        defaultExcludes.add(Object.class);
        defaultExcludes.add(Comparable.class);
        defaultExcludes.add(Serializable.class);
        defaultExcludes.add(Externalizable.class);
        defaultExcludes.add(Cloneable.class);

        manualRules = ArrayListMultimap.create();
        intermediateRules = ArrayListMultimap.create();
        superRules = ArrayListMultimap.create();

        root = ContextImpl.root(this);
    }

    private BindingFunctionBuilder(BindingFunctionBuilder clone) {
        generateRules = clone.generateRules;
        defaultExcludes = new HashSet<Class<?>>(clone.defaultExcludes);
        manualRules = ArrayListMultimap.create(clone.manualRules);
        intermediateRules = ArrayListMultimap.create(clone.intermediateRules);
        superRules = ArrayListMultimap.create(clone.superRules);
        root = ContextImpl.root(this);
    }

    @Override
    public BindingFunctionBuilder clone() {
        return new BindingFunctionBuilder(this);
    }

    /**
     * @return True if bind rules for super and intermediate types should be
     *         generated
     */
    public boolean getGenerateRules() {
        return generateRules;
    }

    /**
     * @return The root context managed by this builder
     */
    public Context getRootContext() {
        return root;
    }

    /**
     * Run the module's {@link Module#configure(Context) bind()} method on the root
     * context of this builder.
     * 
     * @param module The module to apply
     */
    public void applyModule(Module module) {
        module.configure(getRootContext());
    }

    /**
     * Add a type to be excluded from when generating bind rules. This does not
     * invalidate bindings that bind directly to this type.
     * 
     * @param type The type to exclude
     * @throws NullPointerException if type is null
     */
    public void addDefaultExclusion(Class<?> type) {
        if (type == null) {
            throw new NullPointerException("Exclusion type cannot be null");
        }
        defaultExcludes.add(type);
    }

    /**
     * Remove a type that is currently being excluded.
     * 
     * @see #addDefaultExclusion(Class)
     * @param type The type that should no longer be excluded
     * @throws NullPointerException if type is null
     */
    public void removeDefaultExclusion(Class<?> type) {
        if (type == null) {
            throw new NullPointerException("Exclusion type cannot be null");
        }
        defaultExcludes.remove(type);
    }

    /**
     * Return the built BindingFunction for the given RuleSet.
     * 
     * @param set
     * @return
     */
    public BindingFunction build(RuleSet set) {
        return new RuleBasedBindingFunction(getMap(set));
    }

    void addBindRule(RuleSet set, ContextMatcher context, BindRule rule) {
        Multimap<ContextMatcher, BindRule> map = getMap(set);
        map.put(context, rule);
    }

    Set<Class<?>> getDefaultExclusions() {
        return Collections.unmodifiableSet(defaultExcludes);
    }

    private Multimap<ContextMatcher, BindRule> getMap(RuleSet set) {
        switch (set) {
        case EXPLICIT:
            return manualRules;
        case INTERMEDIATE_TYPES:
            return intermediateRules;
        case SUPER_TYPES:
            return superRules;
        default:
            throw new RuntimeException("Should not happen");
        }
    }
}