/*
 * 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.beanhiding;

import static org.junit.Assert.*;

import static test.common.util.Assert.getNonInternalBeanDefinitionCount;

import org.junit.Test;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

import org.springframework.config.java.annotation.Bean;
import org.springframework.config.java.annotation.Configuration;
import org.springframework.config.java.context.JavaConfigApplicationContext;

import test.common.beans.ITestBean;
import test.common.beans.TestBean;

import test.common.config.BaseConfiguration;


/** TODO: JAVADOC */
public class BeanHidingTests {

    @Test
    public void simplestPossibleHidingScenario() {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(SimpleHiding.class);
        TestBean publicBean = ctx.getBean(TestBean.class, "publicBean");
        assertEquals("public", publicBean.getName());
        assertEquals("hidden", publicBean.getSpouse().getName());
        assertFalse("hiddenBean was not hidden!", ctx.containsBean("hiddenBean"));

    }

    @Test
    public void testHidden() {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(BaseConfiguration.class);

        assertTrue(ctx.containsBean(BaseConfiguration.class.getName()));

        ITestBean dependsOnHidden = (ITestBean) ctx.getBean("dependsOnHidden");
        ITestBean hidden = dependsOnHidden.getSpouse();
        assertFalse("hidden bean 'hidden' should not be available via ctx", ctx.containsBean("hidden"));
        assertEquals("hidden", hidden.getName());
        assertEquals("becky", hidden.getSpouse().getName());
        ITestBean hiddenBecky = hidden.getSpouse();
        ITestBean factorysBecky = (ITestBean) ctx.getBean("becky");
        assertSame(hiddenBecky, factorysBecky);
    }

    @Test
    public void testHiddenBeans() {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(HiddenBeansConfig.class);

        BeanFactory bf = ctx.getBeanFactory();
        // hidden beans
        assertFalse(bf.containsBean("protectedBean"));
        assertFalse(bf.containsBean("packageBean"));

        BFAwareBean beans = (BFAwareBean) bf.getBean("beans");
        assertEquals(2, beans.getBeans().length);

        BeanFactory hiddenBF = ((BFAwareBean) beans.getBeans()[1]).bf;
        assertNotSame(bf, hiddenBF);

        assertTrue(hiddenBF.containsBean("protectedBean"));
        assertTrue(hiddenBF.containsBean("packageBean"));
    }

    @Test
    public void testBeanDefinitionCount() throws Exception {
        // 3 @Bean + 1 @Configuration - 2 hidden @Bean
        assertEquals(2, getNonInternalBeanDefinitionCount(new JavaConfigApplicationContext(HiddenBeansConfig.class)));
    }

    @Configuration
    static class SimpleHiding {
        @Bean
        public TestBean publicBean() {
            TestBean publicBean = new TestBean("public");
            publicBean.setSpouse(hiddenBean());
            return publicBean;
        }

        @Bean
        protected TestBean hiddenBean() { return new TestBean("hidden"); }
    }

    @Configuration
    static class HiddenBeansConfig {
        @Bean
        Object packageBean() { return new BFAwareBean("package"); }

        @Bean
        protected Object protectedBean() { return new BFAwareBean("protected"); }

        @Bean
        public BFAwareBean beans() {
            BFAwareBean bean = new BFAwareBean("public");
            bean.setBeans(new Object[] { packageBean(), protectedBean() });
            return bean;
        }
    }

    static class BFAwareBean implements BeanFactoryAware {
        public BeanFactory bf;
        private Object[] beans;

        public BFAwareBean(String name) { }

        public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.bf = beanFactory; }

        public void setBeans(Object[] beans) { this.beans = beans; }

        public Object[] getBeans() { return beans; }

        public BeanFactory getBf() { return bf; }
    }

}
