001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau;
018
019import java.lang.reflect.*;
020import java.util.*;
021import java.util.function.*;
022
023import org.apache.juneau.reflect.*;
024
025/**
026 * Represents a simple settable value.
027 *
028 * <p>
029 * Similar to an {@link Optional} but mutable.
030 *
031 * <h5 class='section'>Notes:</h5><ul>
032 *    <li class='warn'>This class is not thread safe.
033 * </ul>
034 *
035 * <h5 class='section'>See Also:</h5><ul>
036 * </ul>
037 *
038 * @param <T> The value type.
039 */
040public class Value<T> {
041
042   //-----------------------------------------------------------------------------------------------------------------
043   // Static
044   //-----------------------------------------------------------------------------------------------------------------
045
046   /**
047    * Static creator.
048    *
049    * @param <T> The value type.
050    * @param object The object being wrapped.
051    * @return A new {@link Value} object.
052    */
053   public static <T> Value<T> of(T object) {
054      return new Value<>(object);
055   }
056
057   /**
058    * Static creator.
059    *
060    * @param <T> The value type.
061    * @return An empty {@link Value} object.
062    */
063   public static <T> Value<T> empty() {
064      return new Value<>(null);
065   }
066
067   /**
068    * Returns the generic parameter type of the Value type.
069    *
070    * @param t The type to find the parameter type of.
071    * @return The parameter type of the value, or <jk>null</jk> if the type is not a subclass of <c>Value</c>.
072    */
073   public static Type getParameterType(Type t) {
074      if (t instanceof ParameterizedType) {
075         ParameterizedType pt = (ParameterizedType)t;
076         if (pt.getRawType() == Value.class) {
077            Type[] ta = pt.getActualTypeArguments();
078            if (ta.length > 0)
079               return ta[0];
080         }
081      } else if (t instanceof Class) {
082         Class<?> c = (Class<?>)t;
083         if (Value.class.isAssignableFrom(c)) {
084            return ClassInfo.of(c).getParameterType(0, Value.class);
085         }
086      }
087
088      return null;
089   }
090
091   /**
092    * Returns the unwrapped type.
093    *
094    * @param t The type to unwrap.
095    * @return The unwrapped type, or the same type if the type isn't {@link Value}.
096    */
097   public static Type unwrap(Type t) {
098      Type x = getParameterType(t);
099      return x != null ? x : t;
100   }
101
102   /**
103    * Convenience method for checking if the specified type is this class.
104    *
105    * @param t The type to check.
106    * @return <jk>true</jk> if the specified type is this class.
107    */
108   public static boolean isType(Type t) {
109      return
110         (t instanceof ParameterizedType && ((ParameterizedType)t).getRawType() == Value.class)
111         || (t instanceof Class && Value.class.isAssignableFrom((Class<?>)t));
112   }
113
114   //-----------------------------------------------------------------------------------------------------------------
115   // Implementation
116   //-----------------------------------------------------------------------------------------------------------------
117
118
119   private T t;
120   private ValueListener<T> listener;
121
122   /**
123    * Constructor.
124    */
125   public Value() {}
126
127   /**
128    * Constructor.
129    *
130    * @param t Initial value.
131    */
132   public Value(T t) {
133      set(t);
134   }
135
136   /**
137    * Adds a listener for this value.
138    *
139    * @param listener The new listener for this value.
140    * @return This object.
141    */
142   public Value<T> listener(ValueListener<T> listener) {
143      this.listener = listener;
144      return this;
145   }
146
147   /**
148    * Sets the value.
149    *
150    * @param t The new value.
151    * @return This object.
152    */
153   public Value<T> set(T t) {
154      this.t = t;
155      if (listener != null)
156         listener.onSet(t);
157      return this;
158   }
159
160   /**
161    * Sets the value if it's not already set.
162    *
163    * @param t The new value.
164    * @return This object.
165    */
166   public Value<T> setIfEmpty(T t) {
167      if (isEmpty())
168         set(t);
169      return this;
170   }
171
172   /**
173    * Returns the value.
174    *
175    * @return The value, or <jk>null</jk> if it is not set.
176    */
177   public T get() {
178      return t;
179   }
180
181   /**
182    * Returns the value and then unsets it.
183    *
184    * @return The value before it was unset.
185    */
186   public T getAndUnset() {
187      T t2 = t;
188      t = null;
189      return t2;
190   }
191
192   /**
193    * Returns <jk>true</jk> if the value is set.
194    *
195    * @return <jk>true</jk> if the value is set.
196    */
197   public boolean isPresent() {
198      return get() != null;
199   }
200
201   /**
202    * If a value is present, invoke the specified consumer with the value, otherwise do nothing.
203    *
204    * @param consumer Block to be executed if a value is present.
205    */
206   public void ifPresent(Consumer<? super T> consumer) {
207      if (t != null)
208         consumer.accept(t);
209   }
210
211   /**
212    * Applies a mapping function against the contents of this value.
213    *
214    * @param <T2> The mapped value type.
215    * @param mapper The mapping function.
216    * @return The mapped value.
217    */
218   public <T2> Value<T2> map(Function<? super T, T2> mapper) {
219      if (t != null)
220         return Value.of(mapper.apply(t));
221      return Value.empty();
222   }
223
224   /**
225    * Returns the contents of this value or the default value if <jk>null</jk>.
226    *
227    * @param def The default value.
228    * @return The contents of this value or the default value if <jk>null</jk>.
229    */
230   public T orElse(T def) {
231      return t == null ? def : t;
232   }
233   /**
234    * Returns <jk>true</jk> if the value is empty.
235    *
236    * @return <jk>true</jk> if the value is empty.
237    */
238   public boolean isEmpty() {
239      return t == null;
240   }
241
242   /**
243    * Return the value if present, otherwise invoke {@code other} and return
244    * the result of that invocation.
245    *
246    * @param other a {@code Supplier} whose result is returned if no value
247    * is present
248    * @return the value if present otherwise the result of {@code other.get()}
249    * @throws NullPointerException if value is not present and {@code other} is
250    * null
251    */
252   public T orElseGet(Supplier<? extends T> other) {
253      return t != null ? t : other.get();
254   }
255
256   /**
257    * Return the contained value, if present, otherwise throw an exception
258    * to be created by the provided supplier.
259    *
260    * @param <X> The exception type.
261    * @param exceptionSupplier The supplier which will return the exception to
262    * be thrown
263    * @return the present value
264    * @throws X if there is no value present
265    * @throws NullPointerException if no value is present and
266    * {@code exceptionSupplier} is null
267    */
268   public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
269      if (t != null)
270         return t;
271      throw exceptionSupplier.get();
272   }
273
274   @Override /* Object */
275   public String toString() {
276      return "Value("+t+")";
277   }
278}