/*
 * Copyright 2012-2013 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.bootstrap.context.annotation;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.bootstrap.bind.PropertiesConfigurationFactory;
import org.springframework.bootstrap.context.annotation.EnableConfigurationPropertiesImportSelector.ConfigurationPropertiesHolder;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.PropertySources;
import org.springframework.validation.Validator;

/**
 * {@link BeanPostProcessor} to bind {@link PropertySources} to beans annotated with
 * {@link ConfigurationProperties}.
 * 
 * @author Dave Syer
 */
public class PropertySourcesBindingPostProcessor implements BeanPostProcessor {

	private PropertySources propertySources;

	private Validator validator;

	private ConversionService conversionService;

	private DefaultConversionService defaultConversionService = new DefaultConversionService();

	/**
	 * @param propertySources
	 */
	public void setPropertySources(PropertySources propertySources) {
		this.propertySources = propertySources;
	}

	/**
	 * @param validator the validator to set
	 */
	public void setValidator(Validator validator) {
		this.validator = validator;
	}

	/**
	 * @param conversionService the conversionService to set
	 */
	public void setConversionService(ConversionService conversionService) {
		this.conversionService = conversionService;
	}

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		ConfigurationProperties annotation = AnnotationUtils.findAnnotation(
				bean.getClass(), ConfigurationProperties.class);
		Object target = bean;
		if (annotation != null || bean instanceof ConfigurationPropertiesHolder) {
			if (bean instanceof ConfigurationPropertiesHolder) {
				target = ((ConfigurationPropertiesHolder) bean).getTarget();
			}
			PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
					target);
			factory.setPropertySources(this.propertySources);
			factory.setValidator(this.validator);
			// If no explicit conversion service is provided we add one so that (at least)
			// comma-separated arrays of convertibles can be bound automatically
			factory.setConversionService(this.conversionService == null ? this.defaultConversionService
					: this.conversionService);
			String targetName = null;
			if (annotation != null) {
				factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
				factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields());
				targetName = "".equals(annotation.value()) ? (""
						.equals(annotation.name()) ? null : annotation.name())
						: annotation.value();
			}
			factory.setTargetName(targetName);
			try {
				target = factory.getObject(); // throwaway
			} catch (BeansException e) {
				throw e;
			} catch (Exception e) {
				throw new BeanCreationException(beanName, "Could not bind", e);
			}
		}
		return bean;
	}

}

@@ -779,32 +779,207 @@
 ationException;%0A
+import org.springframework.beans.factory.BeanFactory;%0Aimport org.springframework.beans.factory.BeanFactoryAware;%0Aimport org.springframework.beans.factory.ListableBeanFactory;%0A
 import org.sprin
@@ -1029,16 +1029,16 @@
 cessor;%0A
-
 import o
@@ -1345,32 +1345,93 @@
 versionService;%0A
+import org.springframework.core.convert.converter.Converter;%0A
 import org.sprin
@@ -1822,16 +1822,36 @@
 rocessor
+,%0A%09%09BeanFactoryAware
  %7B%0A%0A%09pri
@@ -2061,16 +2061,90 @@
 ice();%0A%0A
+%09private BeanFactory beanFactory;%0A%0A%09private boolean initialized = false;%0A%0A
 %09/**%0A%09 *
@@ -2607,24 +2607,150 @@
 ervice;%0A%09%7D%0A%0A
+%09@Override%0A%09public void setBeanFactory(BeanFactory beanFactory) throws BeansException %7B%0A%09%09this.beanFactory = beanFactory;%0A%09%7D%0A%0A
 %09@Override%0A%09
@@ -3763,14 +3763,12 @@
 l ? 
-this.d
+getD
 efau
@@ -3786,16 +3786,18 @@
 nService
+()
 %0A%09%09%09%09%09: 
@@ -4409,16 +4409,16 @@
 %09%09%7D%0A%09%09%7D%0A
-
 %09%09return
@@ -4428,10 +4428,408 @@
 an;%0A%09%7D%0A%0A
+%09/**%0A%09 * @return%0A%09 */%0A%09private ConversionService getDefaultConversionService() %7B%0A%09%09if (!this.initialized && this.beanFactory instanceof ListableBeanFactory) %7B%0A%09%09%09for (Converter%3C?, ?%3E converter : ((ListableBeanFactory) this.beanFactory)%0A%09%09%09%09%09.getBeansOfType(Converter.class).values()) %7B%0A%09%09%09%09this.defaultConversionService.addConverter(converter);%0A%09%09%09%7D%0A%09%09%7D%0A%09%09return this.defaultConversionService;%0A%09%7D%0A%0A
 %7D%0A

Use available Converters for @ConfigurationProperties binding