/*
 * 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 org.springframework.config.java.annotation.valuesource;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;

import org.junit.Before;
import org.junit.Test;
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.context.JavaConfigApplicationContext;

import test.common.beans.TestBean;
import test.common.dataaccess.DummyDataSource;


/**
 * Unit tests for {@link PropertiesValueSource}.
 * TODO: JAVADOC - rename to match unit
 *
 * @author Chris Beams
 */
@SuppressWarnings("all") // to ignore 'override' warnings
public class PropertyValueSourceTests {
    private static final String JDBC_URL_KEY = "jdbc.url";
    private static final String JDBC_URL_VALUE = "jdbc://foo";

    private static final String JDBC_USERNAME_KEY = "jdbc.username";
    private static final String JDBC_USERNAME_VALUE = "cbeams";

    private static final String REL_DB_PROPS_1 = "db1.properties";
    private static final String REL_DB_PROPS_2 = "db2.properties";

    private static final String BASE_PATH = "org/springframework/config/java/annotation/valuesource/";

    private static final String NPFIX_FQ_DB_PROPS_1 = BASE_PATH + REL_DB_PROPS_1;
    private static final String NPFIX_FQ_DB_PROPS_2 = BASE_PATH + REL_DB_PROPS_2;

    private static final String PFIX_FQ_DB_PROPS_1 = "classpath:" + NPFIX_FQ_DB_PROPS_1;
    private static final String PFIX_FQ_DB_PROPS_2 = "classpath:" + NPFIX_FQ_DB_PROPS_2;

    private JavaConfigApplicationContext ctx;

    @Before
    public void initContext() {
        ctx = null;
    }

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

    @Test
    public void testWithSingleFullyQualifiedLocation() {
        ctx = new JavaConfigApplicationContext(ConfigWithSingleFullyQualifiedLocation.class);
        assertCorrectUrl();
    }

    @Configuration
    @PropertiesValueSource(locations=PFIX_FQ_DB_PROPS_1)
    static abstract class ConfigWithSingleFullyQualifiedLocation extends BaseConfig {
        abstract @ExternalValue(JDBC_URL_KEY) String url();
    }

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

    @Test
    public void testWithMultipleFullyQualifiedLocations() {
        ctx = new JavaConfigApplicationContext(ConfigWithMultipleFullyQualifiedLocations.class);
        assertCorrectUrl();
        assertCorrectUsername();
    }

    @Configuration
    @PropertiesValueSource(locations={PFIX_FQ_DB_PROPS_1, PFIX_FQ_DB_PROPS_2})
    static abstract class ConfigWithMultipleFullyQualifiedLocations extends BaseConfig {
        abstract @ExternalValue(JDBC_URL_KEY) String url();
        abstract @ExternalValue(JDBC_USERNAME_KEY) String username();
    }

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

    @Test
    public void testLocationsDefaultToClasspathResource() {
        ctx = new JavaConfigApplicationContext(ConfigWithNonPrefixedLocation.class);
        assertCorrectUrl();
    }

    @Configuration
    @PropertiesValueSource(locations=NPFIX_FQ_DB_PROPS_1)
    static abstract class ConfigWithNonPrefixedLocation extends BaseConfig {
        abstract @ExternalValue(JDBC_URL_KEY) String url();
    }

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

    @Test
    public void testWithRelativelyQualifiedLocations() {
        ctx = new JavaConfigApplicationContext(ConfigWithRelativeLocations.class);
        assertCorrectUrl();
    }

    @Configuration
    @PropertiesValueSource(locations={REL_DB_PROPS_1, REL_DB_PROPS_2},
                         relativeTo=ConfigWithRelativeLocations.class)
    static abstract class ConfigWithRelativeLocations extends BaseConfig {
        abstract @ExternalValue(JDBC_URL_KEY) String url();
        abstract @ExternalValue(JDBC_URL_KEY) String username();
    }

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

    /**
     * When multiple configuration classes that each declare PropertiesValueSource
     * are used in the same context, each of those valuesources should be registered
     * and iterated through when attempting to resolve a particular value.
     */
    @Test
    public void testRegistrationOfMultipleValueSources() {
        ctx = new JavaConfigApplicationContext(Config1.class, Config2.class);
        assertCorrectUrl();
        //assertCorrectUsername();
    }

    @Configuration
    @PropertiesValueSource(locations=PFIX_FQ_DB_PROPS_1)
    static abstract class Config1 extends BaseConfig {
        abstract @ExternalValue(JDBC_URL_KEY) String url();
        //abstract @ExternalValue(JDBC_URL_KEY) String username();
    }

    @Configuration
    @PropertiesValueSource(locations=PFIX_FQ_DB_PROPS_2)
    static class Config2 {
        public @Bean TestBean testBean() { return new TestBean(); }
    }

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

    static abstract class BaseConfig {
        String url() { return "baseUrl"; }
        String username() { return "baseUsername"; }
        String password() { return "basePassword"; }

        public @Bean DummyDataSource dataSource() {
            return new DummyDataSource(url(), username(), password());
        }
    }

    private void assertCorrectUrl() {
        assertThat(ctx.getBean(DummyDataSource.class).getUrl(), equalTo(JDBC_URL_VALUE));
    }

    private void assertCorrectUsername() {
        assertThat(ctx.getBean(DummyDataSource.class).getUsername(), equalTo(JDBC_USERNAME_VALUE));
    }


}
