001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.context.properties.bind; 018 019import java.lang.annotation.Annotation; 020import java.lang.reflect.Array; 021import java.util.List; 022import java.util.Map; 023import java.util.Set; 024import java.util.function.Supplier; 025 026import org.springframework.core.ResolvableType; 027import org.springframework.core.style.ToStringCreator; 028import org.springframework.util.Assert; 029import org.springframework.util.ObjectUtils; 030 031/** 032 * Source that can be bound by a {@link Binder}. 033 * 034 * @param <T> the source type 035 * @author Phillip Webb 036 * @author Madhura Bhave 037 * @since 2.0.0 038 * @see Bindable#of(Class) 039 * @see Bindable#of(ResolvableType) 040 */ 041public final class Bindable<T> { 042 043 private static final Annotation[] NO_ANNOTATIONS = {}; 044 045 private final ResolvableType type; 046 047 private final ResolvableType boxedType; 048 049 private final Supplier<T> value; 050 051 private final Annotation[] annotations; 052 053 private Bindable(ResolvableType type, ResolvableType boxedType, Supplier<T> value, 054 Annotation[] annotations) { 055 this.type = type; 056 this.boxedType = boxedType; 057 this.value = value; 058 this.annotations = annotations; 059 } 060 061 /** 062 * Return the type of the item to bind. 063 * @return the type being bound 064 */ 065 public ResolvableType getType() { 066 return this.type; 067 } 068 069 /** 070 * Return the boxed type of the item to bind. 071 * @return the boxed type for the item being bound 072 */ 073 public ResolvableType getBoxedType() { 074 return this.boxedType; 075 } 076 077 /** 078 * Return a supplier that provides the object value or {@code null}. 079 * @return the value or {@code null} 080 */ 081 public Supplier<T> getValue() { 082 return this.value; 083 } 084 085 /** 086 * Return any associated annotations that could affect binding. 087 * @return the associated annotations 088 */ 089 public Annotation[] getAnnotations() { 090 return this.annotations; 091 } 092 093 /** 094 * Return a single associated annotations that could affect binding. 095 * @param <A> the annotation type 096 * @param type annotation type 097 * @return the associated annotation or {@code null} 098 */ 099 @SuppressWarnings("unchecked") 100 public <A extends Annotation> A getAnnotation(Class<A> type) { 101 for (Annotation annotation : this.annotations) { 102 if (type.isInstance(annotation)) { 103 return (A) annotation; 104 } 105 } 106 return null; 107 } 108 109 @Override 110 public boolean equals(Object obj) { 111 if (this == obj) { 112 return true; 113 } 114 if (obj == null || getClass() != obj.getClass()) { 115 return false; 116 } 117 Bindable<?> other = (Bindable<?>) obj; 118 boolean result = true; 119 result = result && nullSafeEquals(this.type.resolve(), other.type.resolve()); 120 result = result && nullSafeEquals(this.annotations, other.annotations); 121 return result; 122 } 123 124 @Override 125 public int hashCode() { 126 final int prime = 31; 127 int result = 1; 128 result = prime * result + ObjectUtils.nullSafeHashCode(this.type); 129 result = prime * result + ObjectUtils.nullSafeHashCode(this.annotations); 130 return result; 131 } 132 133 @Override 134 public String toString() { 135 ToStringCreator creator = new ToStringCreator(this); 136 creator.append("type", this.type); 137 creator.append("value", (this.value != null) ? "provided" : "none"); 138 creator.append("annotations", this.annotations); 139 return creator.toString(); 140 } 141 142 private boolean nullSafeEquals(Object o1, Object o2) { 143 return ObjectUtils.nullSafeEquals(o1, o2); 144 } 145 146 /** 147 * Create an updated {@link Bindable} instance with the specified annotations. 148 * @param annotations the annotations 149 * @return an updated {@link Bindable} 150 */ 151 public Bindable<T> withAnnotations(Annotation... annotations) { 152 return new Bindable<>(this.type, this.boxedType, this.value, 153 (annotations != null) ? annotations : NO_ANNOTATIONS); 154 } 155 156 /** 157 * Create an updated {@link Bindable} instance with an existing value. 158 * @param existingValue the existing value 159 * @return an updated {@link Bindable} 160 */ 161 public Bindable<T> withExistingValue(T existingValue) { 162 Assert.isTrue( 163 existingValue == null || this.type.isArray() 164 || this.boxedType.resolve().isInstance(existingValue), 165 () -> "ExistingValue must be an instance of " + this.type); 166 Supplier<T> value = (existingValue != null) ? () -> existingValue : null; 167 return new Bindable<>(this.type, this.boxedType, value, NO_ANNOTATIONS); 168 } 169 170 /** 171 * Create an updated {@link Bindable} instance with a value supplier. 172 * @param suppliedValue the supplier for the value 173 * @return an updated {@link Bindable} 174 */ 175 public Bindable<T> withSuppliedValue(Supplier<T> suppliedValue) { 176 return new Bindable<>(this.type, this.boxedType, suppliedValue, NO_ANNOTATIONS); 177 } 178 179 /** 180 * Create a new {@link Bindable} of the type of the specified instance with an 181 * existing value equal to the instance. 182 * @param <T> the source type 183 * @param instance the instance (must not be {@code null}) 184 * @return a {@link Bindable} instance 185 * @see #of(ResolvableType) 186 * @see #withExistingValue(Object) 187 */ 188 @SuppressWarnings("unchecked") 189 public static <T> Bindable<T> ofInstance(T instance) { 190 Assert.notNull(instance, "Instance must not be null"); 191 Class<T> type = (Class<T>) instance.getClass(); 192 return of(type).withExistingValue(instance); 193 } 194 195 /** 196 * Create a new {@link Bindable} of the specified type. 197 * @param <T> the source type 198 * @param type the type (must not be {@code null}) 199 * @return a {@link Bindable} instance 200 * @see #of(ResolvableType) 201 */ 202 public static <T> Bindable<T> of(Class<T> type) { 203 Assert.notNull(type, "Type must not be null"); 204 return of(ResolvableType.forClass(type)); 205 } 206 207 /** 208 * Create a new {@link Bindable} {@link List} of the specified element type. 209 * @param <E> the element type 210 * @param elementType the list element type 211 * @return a {@link Bindable} instance 212 */ 213 public static <E> Bindable<List<E>> listOf(Class<E> elementType) { 214 return of(ResolvableType.forClassWithGenerics(List.class, elementType)); 215 } 216 217 /** 218 * Create a new {@link Bindable} {@link Set} of the specified element type. 219 * @param <E> the element type 220 * @param elementType the set element type 221 * @return a {@link Bindable} instance 222 */ 223 public static <E> Bindable<Set<E>> setOf(Class<E> elementType) { 224 return of(ResolvableType.forClassWithGenerics(Set.class, elementType)); 225 } 226 227 /** 228 * Create a new {@link Bindable} {@link Map} of the specified key and value type. 229 * @param <K> the key type 230 * @param <V> the value type 231 * @param keyType the map key type 232 * @param valueType the map value type 233 * @return a {@link Bindable} instance 234 */ 235 public static <K, V> Bindable<Map<K, V>> mapOf(Class<K> keyType, Class<V> valueType) { 236 return of(ResolvableType.forClassWithGenerics(Map.class, keyType, valueType)); 237 } 238 239 /** 240 * Create a new {@link Bindable} of the specified type. 241 * @param <T> the source type 242 * @param type the type (must not be {@code null}) 243 * @return a {@link Bindable} instance 244 * @see #of(Class) 245 */ 246 public static <T> Bindable<T> of(ResolvableType type) { 247 Assert.notNull(type, "Type must not be null"); 248 ResolvableType boxedType = box(type); 249 return new Bindable<>(type, boxedType, null, NO_ANNOTATIONS); 250 } 251 252 private static ResolvableType box(ResolvableType type) { 253 Class<?> resolved = type.resolve(); 254 if (resolved != null && resolved.isPrimitive()) { 255 Object array = Array.newInstance(resolved, 1); 256 Class<?> wrapperType = Array.get(array, 0).getClass(); 257 return ResolvableType.forClass(wrapperType); 258 } 259 if (resolved != null && resolved.isArray()) { 260 return ResolvableType.forArrayComponent(box(type.getComponentType())); 261 } 262 return type; 263 } 264 265}