/*
 * Copyright 2012-2017 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.boot.context.properties.bind;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import org.springframework.boot.context.properties.bind.handler.IgnoreErrorsBindHandler;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MockConfigurationPropertySource;
import org.springframework.format.annotation.DateTimeFormat;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;

/**
 * Tests for {@link JavaBeanBinder}.
 *
 * @author Phillip Webb
 * @author Madhura Bhave
 */
public class JavaBeanBinderTests {

	@Rule
	public ExpectedException thrown = ExpectedException.none();

	private List<ConfigurationPropertySource> sources = new ArrayList<>();

	private Binder binder;

	@Before
	public void setup() {
		this.binder = new Binder(this.sources);
	}

	@Test
	public void bindToClassShouldCreateBoundBean() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.int-value", "12");
		source.put("foo.long-value", "34");
		source.put("foo.string-value", "foo");
		source.put("foo.enum-value", "foo-bar");
		this.sources.add(source);
		ExampleValueBean bean = this.binder
				.bind("foo", Bindable.of(ExampleValueBean.class)).get();
		assertThat(bean.getIntValue()).isEqualTo(12);
		assertThat(bean.getLongValue()).isEqualTo(34);
		assertThat(bean.getStringValue()).isEqualTo("foo");
		assertThat(bean.getEnumValue()).isEqualTo(ExampleEnum.FOO_BAR);
	}

	@Test
	public void bindToClassWhenHasNoPrefixShouldCreateBoundBean() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("int-value", "12");
		source.put("long-value", "34");
		source.put("string-value", "foo");
		source.put("enum-value", "foo-bar");
		this.sources.add(source);
		ExampleValueBean bean = this.binder.bind(ConfigurationPropertyName.of(""),
				Bindable.of(ExampleValueBean.class)).get();
		assertThat(bean.getIntValue()).isEqualTo(12);
		assertThat(bean.getLongValue()).isEqualTo(34);
		assertThat(bean.getStringValue()).isEqualTo("foo");
		assertThat(bean.getEnumValue()).isEqualTo(ExampleEnum.FOO_BAR);
	}

	@Test
	public void bindToInstanceShouldBindToInstance() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.int-value", "12");
		source.put("foo.long-value", "34");
		source.put("foo.string-value", "foo");
		source.put("foo.enum-value", "foo-bar");
		this.sources.add(source);
		ExampleValueBean bean = new ExampleValueBean();
		ExampleValueBean boundBean = this.binder
				.bind("foo", Bindable.of(ExampleValueBean.class).withExistingValue(bean))
				.get();
		assertThat(boundBean).isSameAs(bean);
		assertThat(bean.getIntValue()).isEqualTo(12);
		assertThat(bean.getLongValue()).isEqualTo(34);
		assertThat(bean.getStringValue()).isEqualTo("foo");
		assertThat(bean.getEnumValue()).isEqualTo(ExampleEnum.FOO_BAR);
	}

	@Test
	public void bindToInstanceWithNoPropertiesShouldReturnUnbound() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		this.sources.add(source);
		ExampleDefaultsBean bean = new ExampleDefaultsBean();
		BindResult<ExampleDefaultsBean> boundBean = this.binder.bind("foo",
				Bindable.of(ExampleDefaultsBean.class).withExistingValue(bean));
		assertThat(boundBean.isBound()).isFalse();
		assertThat(bean.getFoo()).isEqualTo(123);
		assertThat(bean.getBar()).isEqualTo(456);
	}

	@Test
	public void bindToClassShouldLeaveDefaults() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.bar", "999");
		this.sources.add(source);
		ExampleDefaultsBean bean = this.binder
				.bind("foo", Bindable.of(ExampleDefaultsBean.class)).get();
		assertThat(bean.getFoo()).isEqualTo(123);
		assertThat(bean.getBar()).isEqualTo(999);
	}

	@Test
	public void bindToExistingInstanceShouldLeaveDefaults() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.bar", "999");
		this.sources.add(source);
		ExampleDefaultsBean bean = new ExampleDefaultsBean();
		bean.setFoo(888);
		ExampleDefaultsBean boundBean = this.binder
				.bind("foo",
						Bindable.of(ExampleDefaultsBean.class).withExistingValue(bean))
				.get();
		assertThat(boundBean).isSameAs(bean);
		assertThat(bean.getFoo()).isEqualTo(888);
		assertThat(bean.getBar()).isEqualTo(999);
	}

	@Test
	public void bindToClassShouldBindToMap() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.map.foo-bar", "1");
		source.put("foo.map.bar-baz", "2");
		this.sources.add(source);
		ExampleMapBean bean = this.binder.bind("foo", Bindable.of(ExampleMapBean.class))
				.get();
		assertThat(bean.getMap()).containsExactly(entry(ExampleEnum.FOO_BAR, 1),
				entry(ExampleEnum.BAR_BAZ, 2));
	}

	@Test
	public void bindToClassShouldBindToList() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.list[0]", "foo-bar");
		source.put("foo.list[1]", "bar-baz");
		this.sources.add(source);
		ExampleListBean bean = this.binder.bind("foo", Bindable.of(ExampleListBean.class))
				.get();
		assertThat(bean.getList()).containsExactly(ExampleEnum.FOO_BAR,
				ExampleEnum.BAR_BAZ);
	}

	@Test
	public void bindToListIfUnboundElementsPresentShouldThrowException()
			throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.list[0]", "foo-bar");
		source.put("foo.list[2]", "bar-baz");
		this.sources.add(source);
		this.thrown.expect(BindException.class);
		this.thrown.expectCause(
				Matchers.instanceOf(UnboundConfigurationPropertiesException.class));
		this.binder.bind("foo", Bindable.of(ExampleListBean.class));
	}

	@Test
	public void bindToClassShouldBindToSet() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.set[0]", "foo-bar");
		source.put("foo.set[1]", "bar-baz");
		this.sources.add(source);
		ExampleSetBean bean = this.binder.bind("foo", Bindable.of(ExampleSetBean.class))
				.get();
		assertThat(bean.getSet()).containsExactly(ExampleEnum.FOO_BAR,
				ExampleEnum.BAR_BAZ);
	}

	@Test
	public void bindToClassShouldBindToCollection() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.collection[0]", "foo-bar");
		source.put("foo.collection[1]", "bar-baz");
		this.sources.add(source);
		ExampleCollectionBean bean = this.binder
				.bind("foo", Bindable.of(ExampleCollectionBean.class)).get();
		assertThat(bean.getCollection()).containsExactly(ExampleEnum.FOO_BAR,
				ExampleEnum.BAR_BAZ);
	}

	@Test
	public void bindToClassWhenHasNoSetterShouldBindToMap() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.map.foo-bar", "1");
		source.put("foo.map.bar-baz", "2");
		this.sources.add(source);
		ExampleMapBeanWithoutSetter bean = this.binder
				.bind("foo", Bindable.of(ExampleMapBeanWithoutSetter.class)).get();
		assertThat(bean.getMap()).containsExactly(entry(ExampleEnum.FOO_BAR, 1),
				entry(ExampleEnum.BAR_BAZ, 2));
	}

	@Test
	public void bindToClassWhenHasNoSetterShouldBindToList() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.list[0]", "foo-bar");
		source.put("foo.list[1]", "bar-baz");
		this.sources.add(source);
		ExampleListBeanWithoutSetter bean = this.binder
				.bind("foo", Bindable.of(ExampleListBeanWithoutSetter.class)).get();
		assertThat(bean.getList()).containsExactly(ExampleEnum.FOO_BAR,
				ExampleEnum.BAR_BAZ);
	}

	@Test
	public void bindToClassWhenHasNoSetterShouldBindToSet() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.set[0]", "foo-bar");
		source.put("foo.set[1]", "bar-baz");
		this.sources.add(source);
		ExampleSetBeanWithoutSetter bean = this.binder
				.bind("foo", Bindable.of(ExampleSetBeanWithoutSetter.class)).get();
		assertThat(bean.getSet()).containsExactly(ExampleEnum.FOO_BAR,
				ExampleEnum.BAR_BAZ);
	}

	@Test
	public void bindToClassWhenHasNoSetterShouldBindToCollection() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.collection[0]", "foo-bar");
		source.put("foo.collection[1]", "bar-baz");
		this.sources.add(source);
		ExampleCollectionBeanWithoutSetter bean = this.binder
				.bind("foo", Bindable.of(ExampleCollectionBeanWithoutSetter.class)).get();
		assertThat(bean.getCollection()).containsExactly(ExampleEnum.FOO_BAR,
				ExampleEnum.BAR_BAZ);
	}

	@Test
	public void bindToClassShouldBindNested() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.value-bean.int-value", "123");
		source.put("foo.value-bean.string-value", "foo");
		this.sources.add(source);
		ExampleNestedBean bean = this.binder
				.bind("foo", Bindable.of(ExampleNestedBean.class)).get();
		assertThat(bean.getValueBean().getIntValue()).isEqualTo(123);
		assertThat(bean.getValueBean().getStringValue()).isEqualTo("foo");
	}

	@Test
	public void bindToClassWhenIterableShouldBindNestedBasedOnInstance()
			throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.value-bean.int-value", "123");
		source.put("foo.value-bean.string-value", "foo");
		this.sources.add(source);
		ExampleNestedBeanWithoutSetterOrType bean = this.binder
				.bind("foo", Bindable.of(ExampleNestedBeanWithoutSetterOrType.class))
				.get();
		ExampleValueBean valueBean = (ExampleValueBean) bean.getValueBean();
		assertThat(valueBean.getIntValue()).isEqualTo(123);
		assertThat(valueBean.getStringValue()).isEqualTo("foo");
	}

	@Test
	public void bindToClassWhenNotIterableShouldNotBindNestedBasedOnInstance()
			throws Exception {
		// If we can't tell that binding will happen, we don't want to randomly invoke
		// getters on the class and cause side effects
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.value-bean.int-value", "123");
		source.put("foo.value-bean.string-value", "foo");
		this.sources.add(source.nonIterable());
		BindResult<ExampleNestedBeanWithoutSetterOrType> bean = this.binder.bind("foo",
				Bindable.of(ExampleNestedBeanWithoutSetterOrType.class));
		assertThat(bean.isBound()).isFalse();
	}

	@Test
	public void bindToClassWhenHasNoSetterShouldBindNested() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.value-bean.int-value", "123");
		source.put("foo.value-bean.string-value", "foo");
		this.sources.add(source);
		ExampleNestedBeanWithoutSetter bean = this.binder
				.bind("foo", Bindable.of(ExampleNestedBeanWithoutSetter.class)).get();
		assertThat(bean.getValueBean().getIntValue()).isEqualTo(123);
		assertThat(bean.getValueBean().getStringValue()).isEqualTo("foo");
	}

	@Test
	public void bindToClassWhenHasNoSetterAndImmutableShouldThrowException()
			throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.nested.foo", "bar");
		this.sources.add(source);
		this.thrown.expect(BindException.class);
		this.binder.bind("foo",
				Bindable.of(ExampleImmutableNestedBeanWithoutSetter.class));
	}

	@Test
	public void bindToInstanceWhenNoNestedShouldLeaveNestedAsNull() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("faf.value-bean.int-value", "123");
		this.sources.add(source);
		ExampleNestedBean bean = new ExampleNestedBean();
		BindResult<ExampleNestedBean> boundBean = this.binder.bind("foo",
				Bindable.of(ExampleNestedBean.class).withExistingValue(bean));
		assertThat(boundBean.isBound()).isFalse();
		assertThat(bean.getValueBean()).isNull();
	}

	@Test
	public void bindToClassWhenPropertiesMissingShouldReturnUnbound() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("faf.int-value", "12");
		this.sources.add(source);
		BindResult<ExampleValueBean> bean = this.binder.bind("foo",
				Bindable.of(ExampleValueBean.class));
		assertThat(bean.isBound()).isFalse();
	}

	@Test
	public void bindToClassWhenNoDefaultConstructorShouldReturnUnbound()
			throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.value", "bar");
		this.sources.add(source);
		BindResult<ExampleWithNonDefaultConstructor> bean = this.binder.bind("foo",
				Bindable.of(ExampleWithNonDefaultConstructor.class));
		assertThat(bean.isBound()).isFalse();
	}

	@Test
	public void bindToInstanceWhenNoDefaultConstructorShouldBind() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.value", "bar");
		this.sources.add(source);
		ExampleWithNonDefaultConstructor bean = new ExampleWithNonDefaultConstructor(
				"faf");
		ExampleWithNonDefaultConstructor boundBean = this.binder.bind("foo", Bindable
				.of(ExampleWithNonDefaultConstructor.class).withExistingValue(bean))
				.get();
		assertThat(boundBean).isSameAs(bean);
		assertThat(bean.getValue()).isEqualTo("bar");
	}

	@Test
	public void bindToClassShouldBindHierarchy() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.int-value", "123");
		source.put("foo.long-value", "456");
		this.sources.add(source);
		ExampleSubclassBean bean = this.binder
				.bind("foo", Bindable.of(ExampleSubclassBean.class)).get();
		assertThat(bean.getIntValue()).isEqualTo(123);
		assertThat(bean.getLongValue()).isEqualTo(456);
	}

	@Test
	public void bindToClassWhenPropertyCannotBeConvertedShouldThrowException()
			throws Exception {
		this.sources.add(new MockConfigurationPropertySource("foo.int-value", "foo"));
		this.thrown.expect(BindException.class);
		this.binder.bind("foo", Bindable.of(ExampleValueBean.class));
	}

	@Test
	public void bindToClassWhenPropertyCannotBeConvertedAndIgnoreErrorsShouldNotSetValue()
			throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.int-value", "12");
		source.put("foo.long-value", "bang");
		source.put("foo.string-value", "foo");
		source.put("foo.enum-value", "foo-bar");
		this.sources.add(source);
		IgnoreErrorsBindHandler handler = new IgnoreErrorsBindHandler();
		ExampleValueBean bean = this.binder
				.bind("foo", Bindable.of(ExampleValueBean.class), handler).get();
		assertThat(bean.getIntValue()).isEqualTo(12);
		assertThat(bean.getLongValue()).isEqualTo(0);
		assertThat(bean.getStringValue()).isEqualTo("foo");
		assertThat(bean.getEnumValue()).isEqualTo(ExampleEnum.FOO_BAR);
	}

	@Test
	public void bindToClassWhenMismatchedGetSetShouldBind() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.value", "123");
		this.sources.add(source);
		ExampleMismatchBean bean = this.binder
				.bind("foo", Bindable.of(ExampleMismatchBean.class)).get();
		assertThat(bean.getValue()).isEqualTo("123");
	}

	@Test
	public void bindToClassShouldNotInvokeExtraMethods() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource(
				"foo.value", "123");
		this.sources.add(source.nonIterable());
		ExampleWithThrowingGetters bean = this.binder
				.bind("foo", Bindable.of(ExampleWithThrowingGetters.class)).get();
		assertThat(bean.getValue()).isEqualTo(123);
	}

	@Test
	public void bindToClassWithSelfReferenceShouldBind() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.value", "123");
		this.sources.add(source);
		ExampleWithSelfReference bean = this.binder
				.bind("foo", Bindable.of(ExampleWithSelfReference.class)).get();
		assertThat(bean.getValue()).isEqualTo(123);
	}

	@Test
	public void bindToInstanceWithExistingValueShouldReturnUnbound() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		this.sources.add(source);
		ExampleNestedBean existingValue = new ExampleNestedBean();
		ExampleValueBean valueBean = new ExampleValueBean();
		existingValue.setValueBean(valueBean);
		BindResult<ExampleNestedBean> result = this.binder.bind("foo",
				Bindable.of(ExampleNestedBean.class).withExistingValue(existingValue));
		assertThat(result.isBound()).isFalse();
	}

	@Test
	public void bindWithAnnotations() throws Exception {
		MockConfigurationPropertySource source = new MockConfigurationPropertySource();
		source.put("foo.date", "2014-04-01");
		this.sources.add(source);
		ConverterAnnotatedExampleBean bean = this.binder
				.bind("foo", Bindable.of(ConverterAnnotatedExampleBean.class)).get();
		assertThat(bean.getDate().toString()).isEqualTo("2014-04-01");
	}

	public static class ExampleValueBean {

		private int intValue;

		private long longValue;

		private String stringValue;

		private ExampleEnum enumValue;

		public int getIntValue() {
			return this.intValue;
		}

		public void setIntValue(int intValue) {
			this.intValue = intValue;
		}

		public long getLongValue() {
			return this.longValue;
		}

		public void setLongValue(long longValue) {
			this.longValue = longValue;
		}

		public String getStringValue() {
			return this.stringValue;
		}

		public void setStringValue(String stringValue) {
			this.stringValue = stringValue;
		}

		public ExampleEnum getEnumValue() {
			return this.enumValue;
		}

		public void setEnumValue(ExampleEnum enumValue) {
			this.enumValue = enumValue;
		}

	}

	public static class ExampleDefaultsBean {

		private int foo = 123;

		private int bar = 456;

		public int getFoo() {
			return this.foo;
		}

		public void setFoo(int foo) {
			this.foo = foo;
		}

		public int getBar() {
			return this.bar;
		}

		public void setBar(int bar) {
			this.bar = bar;
		}

	}

	public static class ExampleMapBean {

		private Map<ExampleEnum, Integer> map;

		public Map<ExampleEnum, Integer> getMap() {
			return this.map;
		}

		public void setMap(Map<ExampleEnum, Integer> map) {
			this.map = map;
		}

	}

	public static class ExampleListBean {

		private List<ExampleEnum> list;

		public List<ExampleEnum> getList() {
			return this.list;
		}

		public void setList(List<ExampleEnum> list) {
			this.list = list;
		}

	}

	public static class ExampleSetBean {

		private Set<ExampleEnum> set;

		public Set<ExampleEnum> getSet() {
			return this.set;
		}

		public void setSet(Set<ExampleEnum> set) {
			this.set = set;
		}

	}

	public static class ExampleCollectionBean {

		private Collection<ExampleEnum> collection;

		public Collection<ExampleEnum> getCollection() {
			return this.collection;
		}

		public void setCollection(Collection<ExampleEnum> collection) {
			this.collection = collection;
		}

	}

	public static class ExampleMapBeanWithoutSetter {

		private Map<ExampleEnum, Integer> map = new LinkedHashMap<>();

		public Map<ExampleEnum, Integer> getMap() {
			return this.map;
		}

	}

	public static class ExampleListBeanWithoutSetter {

		private List<ExampleEnum> list = new ArrayList<>();

		public List<ExampleEnum> getList() {
			return this.list;
		}

	}

	public static class ExampleSetBeanWithoutSetter {

		private Set<ExampleEnum> set = new LinkedHashSet<>();

		public Set<ExampleEnum> getSet() {
			return this.set;
		}

	}

	public static class ExampleCollectionBeanWithoutSetter {

		private Collection<ExampleEnum> collection = new ArrayList<>();

		public Collection<ExampleEnum> getCollection() {
			return this.collection;
		}

	}

	public static class ExampleNestedBean {

		private ExampleValueBean valueBean;

		public ExampleValueBean getValueBean() {
			return this.valueBean;
		}

		public void setValueBean(ExampleValueBean valueBean) {
			this.valueBean = valueBean;
		}

	}

	public static class ExampleNestedBeanWithoutSetter {

		private ExampleValueBean valueBean = new ExampleValueBean();

		public ExampleValueBean getValueBean() {
			return this.valueBean;
		}

	}

	public static class ExampleNestedBeanWithoutSetterOrType {

		private ExampleValueBean valueBean = new ExampleValueBean();

		public Object getValueBean() {
			return this.valueBean;
		}

	}

	public static class ExampleImmutableNestedBeanWithoutSetter {

		private NestedImmutable nested = new NestedImmutable();

		public NestedImmutable getNested() {
			return this.nested;
		}

		public static class NestedImmutable {

			public String getFoo() {
				return "foo";
			}

		}

	}

	public static class ExampleWithNonDefaultConstructor {

		private String value;

		public ExampleWithNonDefaultConstructor(String value) {
			this.value = value;
		}

		public String getValue() {
			return this.value;
		}

		public void setValue(String value) {
			this.value = value;
		}

	}

	public abstract static class ExampleSuperClassBean {

		private int intValue;

		public int getIntValue() {
			return this.intValue;
		}

		public void setIntValue(int intValue) {
			this.intValue = intValue;
		}

	}

	public static class ExampleSubclassBean extends ExampleSuperClassBean {

		private long longValue;

		public long getLongValue() {
			return this.longValue;
		}

		public void setLongValue(long longValue) {
			this.longValue = longValue;
		}

	}

	public static class ExampleMismatchBean {

		private int value;

		public String getValue() {
			return String.valueOf(this.value);
		}

		public void setValue(int value) {
			this.value = value;
		}

	}

	public static class ExampleWithThrowingGetters {

		private int value;

		public int getValue() {
			return this.value;
		}

		public void setValue(int value) {
			this.value = value;
		}

		public List<String> getNames() {
			throw new RuntimeException();
		}

		public ExampleValueBean getNested() {
			throw new RuntimeException();
		}

	}

	public static class ExampleWithSelfReference {

		private int value;

		private ExampleWithSelfReference self;

		public int getValue() {
			return this.value;
		}

		public void setValue(int value) {
			this.value = value;
		}

		public ExampleWithSelfReference getSelf() {
			return this.self;
		}

		public void setSelf(ExampleWithSelfReference self) {
			this.self = self;
		}

	}

	public enum ExampleEnum {

		FOO_BAR,

		BAR_BAZ

	}

	public static class ConverterAnnotatedExampleBean {

		@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
		private LocalDate date;

		public LocalDate getDate() {
			return this.date;
		}

		public void setDate(LocalDate date) {
			this.date = date;
		}

	}

}
