/*
 * Copyright 2002-2008 the original author or authors.
 *
 * 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 test.feature.polymorph;

import org.junit.Assert;
import org.junit.Test;
import org.springframework.config.java.annotation.Bean;
import org.springframework.config.java.annotation.Configuration;
import org.springframework.config.java.annotation.ExternalBean;
import org.springframework.config.java.context.JavaConfigApplicationContext;

import test.common.beans.TestBean;


/**
 * A simple set of tests designed to prove that the feature suggested in SJC-24
 * is already implemented.
 *
 * @author Chris Beams
 */
public class PolymorphicConfigurationTests {

    @Test
    public void testConfigurationIsInherited() {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(InheritedConfiguration.class);
        Assert.assertTrue(ctx.containsBean("b"));
    }

    @Configuration
    static class ParentConfiguration { }

    static class InheritedConfiguration extends ParentConfiguration {
        public @Bean TestBean b() { return new TestBean(); }
    }

    // --------------------------------------------------------------------

    @Test
    public void testBeanDeclarationIsInherited() {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(InheritsBeanDeclaration.class);
        Assert.assertTrue(ctx.containsBean("b"));
    }

    static class DeclaresBeanDefinition {
        public @Bean TestBean b() { return new TestBean(); }
    }

    @Configuration
    static class InheritsBeanDeclaration extends DeclaresBeanDefinition { }

    // --------------------------------------------------------------------

    @Test
    public void testExternalBeanIsInherited() {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(ProvidesExternalBean.class, InheritsExternalBean.class);
        Assert.assertTrue(ctx.containsBean("usesExternalBean"));
        Assert.assertEquals("externally.provided", ctx.getBean(TestBean.class, "usesExternalBean").getName());
    }

    // base class does not strictly have to be marked as @Configuration
    static abstract class DeclaresExternalBean {
        // @ExternalBean method cannot be abstract as per convention - it would cause a compilation error in the subclass!
        protected @ExternalBean TestBean b() { return null; }
    }

    @Configuration
    static class ProvidesExternalBean {
        public @Bean TestBean b() { return new TestBean("externally.provided"); }
    }

    @Configuration
    static class InheritsExternalBean extends DeclaresExternalBean {
        public @Bean TestBean usesExternalBean() {
            return new TestBean(b().getName());
        }
    }

}
