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.util.NoSuchElementException; 020import java.util.function.Consumer; 021import java.util.function.Function; 022import java.util.function.Supplier; 023 024import org.springframework.beans.BeanUtils; 025import org.springframework.util.Assert; 026import org.springframework.util.ObjectUtils; 027 028/** 029 * A container object to return the result of a {@link Binder} bind operation. May contain 030 * either a successfully bound object or an empty result. 031 * 032 * @param <T> the result type 033 * @author Phillip Webb 034 * @author Madhura Bhave 035 * @since 2.0.0 036 */ 037public final class BindResult<T> { 038 039 private static final BindResult<?> UNBOUND = new BindResult<>(null); 040 041 private final T value; 042 043 private BindResult(T value) { 044 this.value = value; 045 } 046 047 /** 048 * Return the object that was bound or throw a {@link NoSuchElementException} if no 049 * value was bound. 050 * @return the bound value (never {@code null}) 051 * @throws NoSuchElementException if no value was bound 052 * @see #isBound() 053 */ 054 public T get() throws NoSuchElementException { 055 if (this.value == null) { 056 throw new NoSuchElementException("No value bound"); 057 } 058 return this.value; 059 } 060 061 /** 062 * Returns {@code true} if a result was bound. 063 * @return if a result was bound 064 */ 065 public boolean isBound() { 066 return (this.value != null); 067 } 068 069 /** 070 * Invoke the specified consumer with the bound value, or do nothing if no value has 071 * been bound. 072 * @param consumer block to execute if a value has been bound 073 */ 074 public void ifBound(Consumer<? super T> consumer) { 075 Assert.notNull(consumer, "Consumer must not be null"); 076 if (this.value != null) { 077 consumer.accept(this.value); 078 } 079 } 080 081 /** 082 * Apply the provided mapping function to the bound value, or return an updated 083 * unbound result if no value has been bound. 084 * @param <U> the type of the result of the mapping function 085 * @param mapper a mapping function to apply to the bound value. The mapper will not 086 * be invoked if no value has been bound. 087 * @return an {@code BindResult} describing the result of applying a mapping function 088 * to the value of this {@code BindResult}. 089 */ 090 public <U> BindResult<U> map(Function<? super T, ? extends U> mapper) { 091 Assert.notNull(mapper, "Mapper must not be null"); 092 return of((this.value != null) ? mapper.apply(this.value) : null); 093 } 094 095 /** 096 * Return the object that was bound, or {@code other} if no value has been bound. 097 * @param other the value to be returned if there is no bound value (may be 098 * {@code null}) 099 * @return the value, if bound, otherwise {@code other} 100 */ 101 public T orElse(T other) { 102 return (this.value != null) ? this.value : other; 103 } 104 105 /** 106 * Return the object that was bound, or the result of invoking {@code other} if no 107 * value has been bound. 108 * @param other a {@link Supplier} of the value to be returned if there is no bound 109 * value 110 * @return the value, if bound, otherwise the supplied {@code other} 111 */ 112 public T orElseGet(Supplier<? extends T> other) { 113 return (this.value != null) ? this.value : other.get(); 114 } 115 116 /** 117 * Return the object that was bound, or a new instance of the specified class if no 118 * value has been bound. 119 * @param type the type to create if no value was bound 120 * @return the value, if bound, otherwise a new instance of {@code type} 121 */ 122 public T orElseCreate(Class<? extends T> type) { 123 Assert.notNull(type, "Type must not be null"); 124 return (this.value != null) ? this.value : BeanUtils.instantiateClass(type); 125 } 126 127 /** 128 * Return the object that was bound, or throw an exception to be created by the 129 * provided supplier if no value has been bound. 130 * @param <X> the type of the exception to be thrown 131 * @param exceptionSupplier the supplier which will return the exception to be thrown 132 * @return the present value 133 * @throws X if there is no value present 134 */ 135 public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) 136 throws X { 137 if (this.value == null) { 138 throw exceptionSupplier.get(); 139 } 140 return this.value; 141 } 142 143 @Override 144 public boolean equals(Object obj) { 145 if (this == obj) { 146 return true; 147 } 148 if (obj == null || getClass() != obj.getClass()) { 149 return false; 150 } 151 return ObjectUtils.nullSafeEquals(this.value, ((BindResult<?>) obj).value); 152 } 153 154 @Override 155 public int hashCode() { 156 return ObjectUtils.nullSafeHashCode(this.value); 157 } 158 159 @SuppressWarnings("unchecked") 160 static <T> BindResult<T> of(T value) { 161 if (value == null) { 162 return (BindResult<T>) UNBOUND; 163 } 164 return new BindResult<>(value); 165 } 166 167}