/*
 * 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.grouplens.grapht.annotation.AliasFor;
import org.grouplens.grapht.annotation.AnnotationBuilder;
import org.grouplens.grapht.annotation.DefaultNull;
import org.grouplens.grapht.annotation.DefaultString;
import org.grouplens.grapht.solver.MultipleBindingsException;
import org.junit.Test;

import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Qualifier;
import java.lang.annotation.*;

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;

/**
 * Tests for qualifier aliases.
 *
 * @author <a href="http://www.grouplens.org">GroupLens Research</a>
 */
public class QualifierAliasTest {

    /**
     * Test that bindings and dependencies on unaliased qualifier work.
     */
    @Test
    public void testRealQualifier() throws InjectionException {
        InjectorBuilder bld = InjectorBuilder.create();
        bld.bind(Qual.class, String.class).to("hello");
        Injector inj = bld.build();
        RequireQual rq = inj.getInstance(RequireQual.class);
        assertThat(rq.dependency, equalTo("hello"));
    }

    /**
     * Test that binding to alias w/ dep on unaliased qualifier works.
     */
    @Test
    public void testBindAlias() throws InjectionException {
        InjectorBuilder bld = InjectorBuilder.create();
        bld.bind(Alias.class, String.class).to("hello");
        Injector inj = bld.build();
        RequireQual rq = inj.getInstance(RequireQual.class);
        assertThat(rq.dependency, equalTo("hello"));
    }

    /**
     * Test that binding to alias value w/ dep on unaliased qualifier works.
     */
    @Test
    public void testBindAliasValue() throws InjectionException {
        InjectorBuilder bld = InjectorBuilder.create();
        Alias qual = AnnotationBuilder.of(Alias.class).build();
        bld.bind(String.class).withQualifier(qual).to("hello");
        Injector inj = bld.build();
        RequireQual rq = inj.getInstance(RequireQual.class);
        assertThat(rq.dependency, equalTo("hello"));
    }

    /**
     * Test that binding to qualifier w/ dep on alias works.
     */
    @Test
    public void testRequireAlias() throws InjectionException {
        InjectorBuilder bld = InjectorBuilder.create();
        bld.bind(Qual.class, String.class).to("hello");
        Injector inj = bld.build();
        RequireAlias rq = inj.getInstance(RequireAlias.class);
        assertThat(rq.dependency, equalTo("hello"));
    }

    /**
     * Test that binding and requiring alias works.
     */
    @Test
    public void testBindAndRequireAlias() throws InjectionException {
        InjectorBuilder bld = InjectorBuilder.create();
        bld.bind(Alias.class, String.class).to("hello");
        Injector inj = bld.build();
        RequireAlias rq = inj.getInstance(RequireAlias.class);
        assertThat(rq.dependency, equalTo("hello"));
    }

    /**
     * Test that binding both alias and qualifier fails.
     */
    @Test
    public void testBindQualAndAlias() throws InjectionException {
        InjectorBuilder bld = InjectorBuilder.create();
        bld.bind(Alias.class, String.class).to("goodbye");
        bld.bind(Qual.class, String.class).to("hello");
        Injector inj = bld.build();
        try {
            RequireAlias rq = inj.getInstance(RequireAlias.class);
            fail("should fail w/ multiple bindings");
        } catch (MultipleBindingsException ex) {
            /* expected */
        }
    }

    /**
     * Test that the default on the alias works.
     */
    @Test
    public void testAliasDefault() throws InjectionException {
        InjectorBuilder bld = InjectorBuilder.create();
        Injector inj = bld.build();
        RequireQual rq = inj.getInstance(RequireQual.class);
        assertThat(rq.dependency, equalTo("wombat"));
        RequireAlias ra = inj.getInstance(RequireAlias.class);
        assertThat(ra.dependency, equalTo("wombat"));
    }

    /**
     * Basic qualifier
     */
    @Qualifier
    @Retention(RetentionPolicy.RUNTIME)
    @DefaultString("wombat")
    @Target(ElementType.PARAMETER)
    @Documented
    public static @interface Qual {}

    /**
     * Qualifier Alias
     */
    @Qualifier
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.PARAMETER)
    @Documented
    @AliasFor(Qual.class)
    public static @interface Alias {}

    public static class RequireQual {
        final String dependency;

        @Inject
        public RequireQual(@Qual String dep) {
            dependency = dep;
        }
    }

    public static class RequireAlias {
        final String dependency;

        @Inject
        public RequireAlias(@Alias String dep) {
            dependency = dep;
        }
    }
}

@@ -3899,16 +3899,216 @@
 r fails.
+  Aliases are treated as equivalent to their qualifiers,%0A     * so we have a multiple-binding situation.%0A     *%0A     * We will also take this opportunity to test the %7B@link MultipleBindingsException%7D.
 %0A     */
@@ -4599,24 +4599,243 @@
 expected */%0A
+            assertThat(ex.getBindRules(), hasSize(2));%0A            assertThat(ex.getDesire().getDesiredType(),%0A                       equalTo((Class) String.class));%0A            assertThat(ex.getContext(), hasSize(2));%0A
         %7D%0A  

Test more of the multiple bindings exception