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

import static org.hamcrest.CoreMatchers.equalTo;

import static org.junit.Assert.assertThat;

import static test.common.properties._package.PROPERTIES_PATH;

import org.junit.Test;

import org.springframework.beans.factory.BeanCreationException;

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

import test.common.beans.TestBean;


/**
 * Integration tests for {@link ExternalValue} field support.
 *
 * @author  Chris Beams
 * @see     {@link test.feature.externalvalue.method.ExternalValueMethodTests}
 */
public class ExternalValueFieldTests {

    @Test
    public void simple() {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(Config.class);
        assertThat(ctx.getBean(TestBean.class).getName(), equalTo("Rod"));
    }


    @Test
    public void withHierarchy() {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(SubConfig.class);
        assertThat(ctx.getBean(TestBean.class).getName(), equalTo("Rod"));
    }


    @Test
    public void withDefault() {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(ConfigWithDefault.class);
        assertThat(ctx.getBean(TestBean.class).getName(), equalTo("defaultValue"));
    }


    @Test
    public void withExplicitValue() {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(ConfigWithExplicitValue.class);
        assertThat(ctx.getBean(TestBean.class).getName(), equalTo("cbeams"));
    }


    @Test
    public void withPrivateField() {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(ConfigWithPrivateField.class);
        assertThat(ctx.getBean(TestBean.class).getName(), equalTo("Rod"));
    }


    @Test
    public void withHierarchyAndPrivateField() {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(SubConfigWithPrivateField.class);
        assertThat(ctx.getBean(TestBean.class).getName(), equalTo("Rod"));
    }


    @Test
    public void withHierarchyAndProtectedField() {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(SubConfigWithProtectedField.class);
        assertThat(ctx.getBean(TestBean.class).getName(), equalTo("Rod"));
    }


    @Test(expected = BeanCreationException.class)
    public void noResourceBundlesSpecified() { new JavaConfigApplicationContext(ConfigMissingResourceBundles.class); }


    @Test(expected = BeanCreationException.class)
    public void noValueFound() { new JavaConfigApplicationContext(ConfigWithBogusExternalValue.class); }

    @Configuration
    @PropertiesValueSource(locations=PROPERTIES_PATH)
    public static class Config {
        @ExternalValue
        public String name;

        @Bean
        public TestBean alice() { return new TestBean(name); }
    }

    public static class SubConfig extends Config { }

    @Configuration
    @PropertiesValueSource(locations=PROPERTIES_PATH)
    public static class ConfigWithDefault {
        @ExternalValue
        public String foo = "defaultValue";

        @Bean
        public TestBean testBean() { return new TestBean(foo); }
    }

    @Configuration
    @PropertiesValueSource(locations=PROPERTIES_PATH)
    public static class ConfigWithExplicitValue {
        @ExternalValue("datasource.username")
        public String databaseUrl;

        @Bean
        public TestBean testBean() { return new TestBean(databaseUrl); }
    }

    @Configuration
    @PropertiesValueSource(locations=PROPERTIES_PATH)
    public static class ConfigWithPrivateField {
        @ExternalValue
        private String name;

        @Bean
        public TestBean testBean() { return new TestBean(name); }
    }

    public static class SubConfigWithPrivateField extends ConfigWithPrivateField { }

    @Configuration
    @PropertiesValueSource(locations=PROPERTIES_PATH)
    public static class ConfigWithProtectedField {
        @ExternalValue
        protected String name;
    }

    public static class SubConfigWithProtectedField extends ConfigWithProtectedField {
        @Bean
        public TestBean testBean() { return new TestBean(name); }
    }

    @Configuration
    public static class ConfigMissingResourceBundles {
        @ExternalValue
        private String name;

        @Bean
        public TestBean testBean() { return new TestBean(name); }
    }

    @Configuration
    @PropertiesValueSource(locations=PROPERTIES_PATH)
    public static class ConfigWithBogusExternalValue {
        @ExternalValue
        private String bogus; // won't be found in the props file

        @Bean
        public TestBean testBean() { return new TestBean(bogus); }
    }

}
