001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2022 microBean™. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * 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 014 * implied. See the License for the specific language governing 015 * permissions and limitations under the License. 016 */ 017package org.microbean.loader.spi; 018 019import java.lang.reflect.GenericArrayType; 020import java.lang.reflect.ParameterizedType; 021import java.lang.reflect.Type; 022 023import java.util.NoSuchElementException; 024import java.util.Objects; 025 026import java.util.function.Supplier; 027 028import org.microbean.development.annotation.Convenience; 029 030import org.microbean.invoke.FixedValueSupplier; 031import org.microbean.invoke.OptionalSupplier; 032 033import org.microbean.path.Path; 034 035import org.microbean.qualifier.Qualifiers; 036 037import org.microbean.type.JavaTypes; 038 039/** 040 * An {@link OptionalSupplier} of a value that is additionally 041 * qualified by a {@link Path} partially identifying the kinds of 042 * {@link Qualifiers} and {@link Path}s for which it might be 043 * suitable. 044 * 045 * <p>{@link Value}s are typically returned by {@link Provider} 046 * implementations.</p> 047 * 048 * <p>A {@link Value} once received retains no reference to whatever 049 * produced it and can be regarded as an authoritative source for 050 * (possibly ever-changing) values going forward. Notably, it can be 051 * cached.</p> 052 * 053 * @param <T> the type of value this {@link Value} returns 054 * 055 * @author <a href="https://about.me/lairdnelson" 056 * target="_parent">Laird Nelson</a> 057 * 058 * @see OptionalSupplier 059 * 060 * @see Provider 061 */ 062public final class Value<T> implements OptionalSupplier<T> { 063 064 065 /* 066 * Instance fields. 067 */ 068 069 070 private final Path<? extends Type> path; 071 072 private final OptionalSupplier<? extends T> supplier; 073 074 075 /* 076 * Constructors. 077 */ 078 079 080 /** 081 * Creates a new {@link Value} <strong>that returns a valid {@code 082 * null} value from its {@link #get() get()} method</strong>. 083 * 084 * @param path the {@link Path}, possibly relative, for which this 085 * {@link Value} is suitable; must not be {@code null} 086 * 087 * @exception NullPointerException if {@code path} is {@code null} 088 * 089 * @see #get() 090 * 091 * @see FixedValueSupplier#of(Object) 092 */ 093 public Value(final Path<? extends Type> path) { 094 this(FixedValueSupplier.of(null), path); 095 } 096 097 /** 098 * Creates a new {@link Value} that will forever and always return 099 * the supplied {@code value} from its {@link #get() get()} method. 100 * 101 * @param path the {@link Path}, possibly relative, for which this 102 * {@link Value} is suitable; must not be {@code null} 103 * 104 * @param value a fixed value to be returned by the {@link #get()} 105 * method whenever it is invoked; may be {@code null} 106 * 107 * @exception NullPointerException if {@code path} is {@code null} 108 * 109 * @see #get() 110 * 111 * @see FixedValueSupplier#of(Object) 112 */ 113 public Value(final T value, final Path<? extends Type> path) { 114 this(FixedValueSupplier.of(value), path); 115 } 116 117 /** 118 * Creates a new {@link Value}. 119 * 120 * @param source the {@link Value} to use as the primary supplier; 121 * must not be {@code null} 122 * 123 * @param defaults the {@link Supplier} to use as the fallback; may 124 * be {@code null} 125 * 126 * @exception NullPointerException if {@code source} is {@code null} 127 */ 128 public Value(final Value<? extends T> source, final Supplier<? extends T> defaults) { 129 this(OptionalSupplier.of(source, defaults), source.path()); 130 } 131 132 /** 133 * Creates a new {@link Value}. 134 * 135 * @param supplier the actual {@link Supplier} that will return 136 * values; may be {@code null} 137 * 138 * @param path the {@link Path}, possibly relative, for which this 139 * {@link Value} is suitable; must not be {@code null} 140 * 141 * @exception NullPointerException if {@code path} is {@code null} 142 */ 143 public Value(final Supplier<? extends T> supplier, final Path<? extends Type> path) { 144 super(); 145 this.supplier = OptionalSupplier.of(supplier); 146 this.path = Objects.requireNonNull(path, "path"); 147 } 148 149 150 /* 151 * Instance methods. 152 */ 153 154 155 /** 156 * Returns a {@link Value} with this {@link Value}'s supplier and 157 * the supplied {@link Path}. 158 * 159 * @param path the new {@link Path}; must not be {@code null} 160 * 161 * @return a {@link Value} with this {@link Value}'s supplier and 162 * the supplied {@link Path} 163 * 164 * @exception NullPointerException if {@code path} is {@code null} 165 * 166 * @nullability This method never returns {@code null}. 167 * 168 * @idempotency This method is idempotent and deterministic. 169 * 170 * @threadsafety This method is safe for concurrent use by multiple 171 * threads. 172 */ 173 public final Value<T> with(final Path<? extends Type> path) { 174 if (path.equals(this.path())) { 175 return this; 176 } else { 177 return new Value<>(this.supplier, path); 178 } 179 } 180 181 /** 182 * Returns the {@link Qualifiers} with which this {@link Value} is 183 * associated. 184 * 185 * @return the {@link Qualifiers} with which this {@link Value} is 186 * associated 187 * 188 * @nullability This method never returns {@code null}. 189 * 190 * @idempotency This method is idempotent and deterministic. 191 * 192 * @threadsafety This method is safe for concurrent use by multiple 193 * threads. 194 * 195 * @see Path#qualifiers() 196 */ 197 @Convenience 198 public final Qualifiers<? extends String, ?> qualifiers() { 199 return this.path().qualifiers(); 200 } 201 202 /** 203 * Returns the {@link Path} with which this {@link Value} is 204 * associated. 205 * 206 * @return the {@link Path} with which this {@link Value} is 207 * associated 208 * 209 * @nullability This method never returns {@code null}. 210 * 211 * @idempotency This method is idempotent and deterministic. 212 * 213 * @threadsafety This method is safe for concurrent use by multiple 214 * threads. 215 */ 216 public final Path<? extends Type> path() { 217 return this.path; 218 } 219 220 /** 221 * Invokes the {@link Supplier#get() get()} method of the {@link 222 * Supplier} supplied at {@linkplain #Value(Supplier, Path) 223 * construction time} and returns its value, which may be {@code 224 * null}. 225 * 226 * @return tbe return value of an invocation of the {@link 227 * OptionalSupplier#get() get()} method of the {@link Supplier} 228 * supplied at {@linkplain #Value(Supplier, Path) construction 229 * time}, which may be {@code null} 230 * 231 * @exception NoSuchElementException if this method should no longer 232 * be invoked because there is no chance it will ever produce a 233 * suitable value again 234 * 235 * @exception UnsupportedOperationException if this method should no 236 * longer be invoked because there is no chance it will ever produce 237 * a suitable value again 238 * 239 * @see #Value(Supplier, Path) 240 * 241 * @nullability This method may return {@code null}. 242 * 243 * @threadsafety This method is safe for concurrent use by multiple 244 * threads, provided that the {@link Supplier} supplied at 245 * {@linkplain #Value(Supplier, Path) construction time} is also 246 * safe for concurrent use by multiple threads. 247 * 248 * @idempotency This method is as idempotent and deterministic as 249 * the {@link Supplier} supplied at {@linkplain #Value(Supplier, 250 * Path) construction time}. 251 */ 252 @Override // Supplier<T> 253 public final T get() { 254 return this.supplier.get(); 255 } 256 257 /** 258 * Returns an appropriate {@link Determinism} for this {@link 259 * Value}. 260 * 261 * @return an appropriate {@link Determinism} for this {@link Value} 262 * 263 * @nullability This method never returns {@code null}. 264 * 265 * @idempotency This method is idempotent and deterministic. 266 * 267 * @threadsafety This method is safe for concurrent use by multiple 268 * threads. 269 */ 270 @Override // OptionalSupplier<T> 271 public final Determinism determinism() { 272 return this.supplier.determinism(); 273 } 274 275 /** 276 * Returns the result of invoking {@link Path#qualified()} on the 277 * return value of this {@link Value}'s {@link #path()} method. 278 * 279 * <p>This method never returns {@code null}.</p> 280 * 281 * @return the result of invoking {@link Path#qualified()} on the 282 * return value of this {@link Value}'s {@link #path()} method; 283 * never {@code null} 284 * 285 * @nullability This method never returns {@code null}. 286 * 287 * @idempotency This method is idempotent and and deterministic. 288 * 289 * @threadsafety This method is safe for concurrent use by 290 * multiple threads. 291 * 292 * @see #path() 293 * 294 * @see Path#qualified() 295 */ 296 @Convenience 297 public final Type type() { 298 return this.path().qualified(); 299 } 300 301 /** 302 * Returns the type erasure of the return value of the {@link 303 * #type()} method. 304 * 305 * <p>This method never returns {@code null}.</p> 306 * 307 * @return the result of invoking {@link JavaTypes#erase(Type)} on 308 * the return value of this {@link Value}'s {@link #type()} method; 309 * never {@code null} 310 * 311 * @exception IllegalStateException if somehow the return value of 312 * {@link #type()} could not be erased 313 * 314 * @nullability This method never returns {@code null}. 315 * 316 * @idempotency This method is idempotent and and deterministic. 317 * 318 * @threadsafety This method is safe for concurrent use by 319 * multiple threads. 320 * 321 * @see #path() 322 * 323 * @see JavaTypes#erase(Type) 324 */ 325 public final Class<T> typeErasure() { 326 @SuppressWarnings("unchecked") 327 final Class<T> returnValue = (Class<T>)JavaTypes.erase(this.type()); 328 if (returnValue == null) { 329 throw new IllegalStateException(); 330 } 331 return returnValue; 332 } 333 334 /** 335 * Returns a hashcode for this {@link Value} calculated from its 336 * {@link #path() Path}, and {@linkplain #determinism() whether it 337 * is deterministic}. 338 * 339 * @return a hashcode for this {@link Value} 340 * 341 * @idempotency This method is idempotent and and deterministic. 342 * 343 * @threadsafety This method is safe for concurrent use by 344 * multiple threads. 345 * 346 * @see #equals(Object) 347 * 348 * @see #path() 349 * 350 * @see #determinism() 351 */ 352 @Override // Object 353 public final int hashCode() { 354 int hashCode = 17; 355 356 Object v = this.path(); 357 int c = v == null ? 0 : v.hashCode(); 358 hashCode = 37 * hashCode + c; 359 360 v = this.determinism(); 361 c = v == null ? 0 : v.hashCode(); 362 hashCode = 37 * hashCode + c; 363 364 return hashCode; 365 } 366 367 /** 368 * Returns {@code true} if this {@link Value} is equal to the 369 * supplied {@link Object}. 370 * 371 * <p>This method will return {@code true} if and only if the 372 * following conditions hold:</p> 373 * 374 * <ul> 375 * 376 * <li>The supplied {@link Object} is not {@code null}</li> 377 * 378 * <li>The supplied {@link Object}'s {@link Object#getClass()} 379 * method returns {@link Value Value.class}</li> 380 * 381 * <li>{@link Objects#equals(Object, Object) 382 * Objects.equals(this.path(), otherValue.path())} returns {@code 383 * true}</li> 384 * 385 * <li>{@link Objects#equals(Object, Object) 386 * Objects.equals(this.determinism(), otherValue.determinism())} 387 * returns {@code true}</li> 388 * 389 * </ul> 390 * 391 * @param other the {@link Object} to test; may be {@code null} in 392 * which case {@code false} will be returned 393 * 394 * @return {@code true} if this {@link Value} is equal to the 395 * supplied {@link Object}; {@code false} otherwise 396 * 397 * @idempotency This method is idempotent and and deterministic. 398 * 399 * @threadsafety This method is safe for concurrent use by 400 * multiple threads. 401 * 402 * @see #path() 403 * 404 * @see #determinism() 405 */ 406 @Override // Object 407 public final boolean equals(final Object other) { 408 if (other == this) { 409 return true; 410 } else if (other != null && this.getClass() == other.getClass()) { 411 final Value<?> her = (Value<?>)other; 412 return 413 Objects.equals(this.path(), her.path()) && 414 Objects.equals(this.determinism(), her.determinism()); 415 } else { 416 return false; 417 } 418 } 419 420}