blob: 0cd3d03746818b0848302750d09dd0cd039dfa4e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.brooklyn.util.guava;
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.Serializable;
import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
import java.util.function.Consumer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.brooklyn.util.javalang.JavaClassNames;
import org.apache.brooklyn.util.javalang.MemoryUsageTracker.SoftUsageTracker;
import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.ImmutableSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Like Guava Optional but permitting null and permitting errors to be thrown. */
public abstract class Maybe<T> implements Serializable, Supplier<T> {
private static final Logger LOG = LoggerFactory.getLogger(Maybe.class);
private static final long serialVersionUID = -6372099069863179019L;
/** Returns an absent indicator. No message is available and access does not include any reference to this creation.
* Therefore it is fast and simple, but hard to work with if someone might {@link #get()} it and want a useful exception.
* See also {@link #absentNoTrace(String)} to include a message with very low overhead,
* or {@link #absentWithTrace(String)} or {@link #absent(Throwable)} for more control over the exception thrown.
*/
public static <T> Maybe<T> absent() {
return new Maybe.Absent<T>();
}
/** Convenience for {@link #absentWithTrace(String)}. */
public static <T> Maybe<T> absent(final String message) {
return absent(new IllegalStateException(message));
}
/** Creates an absent whose {@link #get()} throws an {@link IllegalStateException} with the indicated message.
* Both stack traces (the cause and the callers) are provided, which can be quite handy,
* but comparatively expensive as the cause stack trace has to generated when this method is invoked,
* even if it is never accessed. See also {@link #absentNoTrace(String)} and {@link #absent(Throwable)}. */
public static <T> Maybe<T> absentWithTrace(final String message) {
return absent(new IllegalStateException(message));
}
/** Creates an absent whose get throws an {@link IllegalStateException} with the indicated message,
* but not a stack trace for the calling location. As stack traces can be comparatively expensive
* this is useful for efficiency, but it can make debugging harder as the origin of the absence is not kept,
* in contrast to {@link #absentWithTrace(String)} and {@link #absent(Throwable)}. */
public static <T> Maybe<T> absentNoTrace(final String message) {
return absent(new IllegalStateExceptionSupplier(message));
}
/** As {@link #absentWithTrace(String)} but using the provided exception instead of this location
* as the cause, and a string based on this cause as the message on the {@link IllegalStateException}
* thrown if a user does a {@link #get()}.
* Useful if an {@link Exception} has already been generated (and no overhead)
* or if you want to supply a specific cause as in <code>absent(new MyException(...))</code>
* (but there is the Exception creation overhead there). */
public static <T> Maybe<T> absent(final Throwable cause) {
return absent(new IllegalStateExceptionSupplier(cause));
}
/** As {@link #absent(Throwable)} but using the given message as the message on the {@link IllegalStateException}
* thrown if a user does a {@link #get()}. */
public static <T> Maybe<T> absent(final String message, final Throwable cause) {
return absent(new IllegalStateExceptionSupplier(message, cause));
}
/** Creates an absent whose {@link #get()} throws a {@link RuntimeException}
* generated on demand from the given supplier */
public static <T> Maybe<T> absent(final Supplier<? extends RuntimeException> exceptionSupplier) {
return new Absent<T>(Preconditions.checkNotNull(exceptionSupplier));
}
/** as {@link #absentNull(String)} but with a generic message */
public static <T> Maybe<T> absentNull() {
return absentNull("disallowed null value");
}
/** like {@link #absent(String)} but {@link #isNull()} will return true on the result. */
public static <T> Maybe<T> absentNull(String message) {
return new AbsentNull<T>(message);
}
/** Creates a new Maybe object which is present.
* The argument may be null and the object still present,
* which may be confusing in some contexts
* (traditional {@link Optional} usages) but
* may be natural in others (where null is a valid value, distinguished from no value set).
* See also {@link #ofDisallowingNull(Object)}. */
public static <T> Maybe<T> ofAllowingNull(@Nullable T value) {
return new Present<T>(value);
}
/** Creates a new Maybe object which is present if and only if the argument is not null.
* If the argument is null, then an {@link #absentNull()} is returned,
* on which {@link #isNull()} will be true. */
public static <T> Maybe<T> ofDisallowingNull(@Nullable T value) {
if (value==null) return absentNull();
return new Present<T>(value);
}
/** Creates a new Maybe object.
* Currently this uses {@link #ofAllowingNull(Object)} semantics,
* but it is recommended to use that method for clarity
* if the argument might be null. */
// note: Optional throws if null is supplied; we might want to do the same here
public static <T> Maybe<T> of(@Nullable T value) {
return ofAllowingNull(value);
}
/**
* Casts the given value to the desired super-type. This is valid because {@link Maybe} is immutable,
* so things like {@code Maybe<Object>} is a super-type of {@code Maybe<String>}.
*/
@SuppressWarnings("unchecked")
public static <T> Maybe<T> cast(Maybe<? extends T> value) {
return (Maybe<T>) value;
}
/** Like {@link #cast(Maybe)} but allows any casting because that is valid for absents.
* Enforces that the argument really is absent (or null). */
@SuppressWarnings("unchecked")
public static <T> Maybe<T> castAbsent(Maybe<?> absent) {
if (absent!=null && absent.isPresent() && !absent.isNull()) {
throw new IllegalArgumentException("Expected an absent, but instead got: "+absent);
}
return (Maybe<T>)absent;
}
/** Converts the given {@link Maybe} to {@link Optional} (guava), failing if this {@link Maybe} contains null.
* Recommend use {@link #toGuavaOptional()} instead to avoid confusion. */
public Optional<T> toOptional() {
return toGuavaOptional();
}
/** Converts the given {@link Maybe} to {@link Optional}, failing if this {@link Maybe} contains null. */
public Optional<T> toGuavaOptional() {
if (isPresent()) return Optional.of(get());
return Optional.absent();
}
/** Converts the given {@link Maybe} to {@link java.util.Optional}, failing if this {@link Maybe} contains null. */
public java.util.Optional<T> toJavaOptional() {
if (isPresent()) return java.util.Optional.of(get());
return java.util.Optional.empty();
}
/** Creates a new Maybe object using {@link #ofDisallowingNull(Object)} semantics.
* It is recommended to use that method for clarity.
* This method is provided for consistency with {@link Optional#fromNullable(Object)}. */
public static <T> Maybe<T> fromNullable(@Nullable T value) {
return ofDisallowingNull(value);
}
/** Creates a new Maybe object out of the {@link Optional} argument */
public static <T> Maybe<T> fromOptional(Optional<T> value) {
return new MaybeGuavaOptional<>(value);
}
/** Creates a new Maybe object out of the {@link Optional} argument */
public static <T> Maybe<T> fromOptional(java.util.Optional<T> value) {
return new MaybeJavaOptional<>(value);
}
/** creates an instance wrapping a {@link SoftReference}, so it might go absent later on.
* if null is supplied the result is a present null. */
public static <T> Maybe<T> soft(@Nonnull T value) {
return softThen(value, null);
}
/** creates an instance wrapping a {@link SoftReference}, using the second item given
* if the first argument is dereferenced.
* however if the first argument is null, this is a permanent present null,
* as {@link #of(Object)} with null. */
public static <T> Maybe<T> softThen(T value, Maybe<T> ifEmpty) {
if (value==null) return of((T)null);
return new SoftlyPresent<T>(value).usingAfterExpiry(ifEmpty);
}
public static <T> Maybe<T> of(final Optional<T> value) {
return fromOptional(value);
}
private static <T> Maybe<T> ofOldKeptForDeserializationOfAnonymousInnerClass(final Optional<T> value) {
if (value.isPresent()) return new AbstractPresent<T>() {
private static final long serialVersionUID = -5735268814211401356L;
@Override
public T get() {
return value.get();
}
};
return absent();
}
public static <T> Maybe<T> of(final java.util.Optional<T> value) {
return fromOptional(value);
}
@SuppressWarnings("unused")
private static <T> Maybe<T> ofOldKeptForDeserializationOfAnonymousInnerClass(final Supplier<T> value) {
return new AbstractPresent<T>() {
private static final long serialVersionUID = -5735268814211401356L;
@Override
public T get() {
return value.get();
}
};
}
public <T2> Maybe<T2> asType(Class<T2> requiredClass) {
if (isAbsent()) return Maybe.castAbsent(this);
if (requiredClass.isInstance(get())) return Maybe.<T2>cast((Maybe)this);
return Maybe.absent(() -> new IllegalArgumentException("Value is not of required type "+requiredClass));
}
public static class MaybeGuavaOptional<T> extends Maybe<T> {
private static final long serialVersionUID = -823731500051341455L;
private final Optional<T> value;
public MaybeGuavaOptional(Optional<T> value) {
this.value = value;
}
@Override
public T get() {
return value.get();
}
@Override
public boolean isNull() { return false; }
public Optional<T> getOptional() {
return value;
}
@Override
public boolean isPresent() { return value.isPresent(); }
}
public static class MaybeJavaOptional<T> extends Maybe<T> {
private static final long serialVersionUID = -823731500051341455L;
private final java.util.Optional<T> value;
public MaybeJavaOptional(java.util.Optional<T> value) {
this.value = value;
}
@Override
public T get() {
return value.get();
}
@Override
public boolean isNull() { return false; }
public java.util.Optional<T> getOptional() {
return value;
}
@Override
public boolean isPresent() { return value.isPresent(); }
}
public static <T> Maybe<T> of(final Supplier<T> value) {
return new MaybeSupplier<T>(value);
}
public static class MaybeSupplier<T> extends AbstractPresent<T> {
private static final long serialVersionUID = -823731500051341455L;
private final Supplier<T> supplier;
public MaybeSupplier(Supplier<T> value) {
this.supplier = value;
}
@Override
public T get() {
return supplier.get();
}
public Supplier<T> getSupplier() {
return supplier;
}
}
/** returns a Maybe containing the next element in the iterator, or absent if none */
public static <T> Maybe<T> next(Iterator<T> iterator) {
return iterator.hasNext() ? Maybe.of(iterator.next()) : Maybe.<T>absent();
}
public abstract boolean isPresent();
@Override
public abstract T get();
public boolean isAbsent() {
return !isPresent();
}
public boolean isAbsentOrNull() {
return isAbsent() || isNull();
}
public boolean isPresentAndNonNull() {
return isPresent() && !isNull();
}
/** Whether the value is null, if present, or
* if it was specified as absent because it was null,
* e.g. using {@link #fromNullable(Object)}.
*/
public abstract boolean isNull();
public T or(T nextValue) {
if (isPresent()) return get();
return nextValue;
}
public Maybe<T> or(Maybe<T> nextValue) {
if (isPresent()) return this;
return nextValue;
}
public T or(Supplier<T> nextValue) {
if (isPresent()) return get();
return nextValue.get();
}
public Maybe<T> orMaybe(Supplier<Maybe<T>> nextValue) {
if (isPresent()) return this;
return nextValue.get();
}
public T orNull() {
if (isPresent()) return get();
return null;
}
/** As {@link #get()} but if the Maybe wraps an exception
* (and where {@link #get()} throws a {@link RuntimeException} indicating the caller,
* caused by the exception in original processing)
* this throws the original unwrapped exception
* (masking the caller's location)
* <p>
* As this masks the caller's exception, use with care, typically in a location
* near the original execution, otherwise it can get confusing.
* For instance <code>someCallReturningMaybe().orThrowUnwrapped()</code> is a nice idiom,
* but <code>someMaybeVarReturnedEarlier.orThrowUnwrapped()</code> is usually a bad idea.
* <p>
* The benefit of this is simpler stack traces and preservation of original exception type.
*/
public T orThrowUnwrapped() {
// note: the difference in behaviour to get() is in the Absent subclass
return get();
}
public Maybe<T> orThrowing(String message) {
return or(Maybe.absent(message));
}
public Maybe<T> orThrowing(Throwable t) {
return or(Maybe.absent(t));
}
public Maybe<T> orThrowing(Supplier<RuntimeException> t) {
return or(Maybe.absent(t));
}
public T orThrow(String message) {
return orThrowing(message).get();
}
public T orThrow(Throwable t) {
return orThrowing(t).get();
}
public T orThrow(Supplier<RuntimeException> t) {
return orThrowing(t).get();
}
public Set<T> asSet() {
if (isPresent()) return ImmutableSet.of(get());
return Collections.emptySet();
}
/** alias for {@link #map(Function)} - does lazy conversion */
public <V> Maybe<V> transform(final Function<? super T, V> f) {
return map(f);
}
/** lazy conversion if a value is present, otherwise preserves the absence;
* see {@link #transformNow(Function)} for immediate conversion */
public <V> Maybe<V> map(final java.util.function.Function<? super T, V> f) {
return new MaybeTransforming(this, f);
}
public <V> Maybe<V> map(final Function<? super T, V> f) {
return new MaybeTransforming(this, f);
}
public <V> Maybe<V> mapMaybe(final Function<? super T, Maybe<V>> f) {
return new MaybeTransformingMaybe(this, f);
}
private <V> Maybe<V> mapKeptForDeserializingOld(final Function<? super T, V> f) {
if (isPresent()) return new AbstractPresent<V>() {
private static final long serialVersionUID = 325089324325L;
@Override
public V get() {
return f.apply(Maybe.this.get());
}
};
return (Maybe<V>)this;
}
public static class MaybeTransforming<T,V> extends Maybe<V> {
private static final long serialVersionUID = 325089324325L;
private final Maybe<T> input;
private final java.util.function.Function<? super T, V> f;
public MaybeTransforming(Maybe<T> input, java.util.function.Function<? super T,V> f) {
this.input = input;
this.f = f;
}
@Override
public boolean isPresent() {
return input.isPresent();
}
@Override
public V get() {
return f.apply(input.get());
}
@Override
public boolean isNull() {
return isPresent() ? get()==null : input.isNull();
}
}
public static class MaybeTransformingMaybe<T,V> extends Maybe<V> {
private static final long serialVersionUID = 325089324325L;
private final Maybe<T> input;
private final Function<? super T, Maybe<V>> f;
private boolean gotten = false;
private Maybe<V> gottenObject;
public MaybeTransformingMaybe(Maybe<T> input, Function<? super T,Maybe<V>> f) {
this.input = input;
this.f = f;
}
@Override
public boolean isPresent() {
if (!input.isPresent()) return false;
evaluate();
return gottenObject.isPresent();
}
public void evaluate() {
if (!gotten) {
synchronized (this) {
if (!gotten) {
gotten = true;
if (!input.isPresent()) {
gottenObject = Maybe.castAbsent(input);
return;
}
gottenObject = f.apply(input.get());
if (gottenObject==null) {
gottenObject = Maybe.absent("transformation yielded null rather than a maybe");
}
}
}
}
}
@Override
public V get() {
evaluate();
return gottenObject.get();
}
@Override
public boolean isNull() {
return isPresent() ? get()==null : input.isNull();
}
}
/** applies a function immediately if a value is present and returns the transformed object,
* or returns the original absence */
public <V> Maybe<V> transformNow(final Function<? super T, V> f) {
if (isPresent()) return Maybe.of(f.apply(Maybe.this.get()));
return (Maybe<V>)this;
}
/** applies a consumer immediately if a value is present;
* returns the same object for convenience (as obviously there is no result from the consumer) */
public Maybe<T> apply(final Consumer<? super T> f) {
if (isPresent()) {
f.accept(Maybe.this.get());
}
return this;
}
/**
* Returns the value of each present instance from the supplied {@code maybes}, in order,
* skipping over occurrences of {@link Maybe#absent()}. Iterators are unmodifiable and are
* evaluated lazily.
*
* @see Optional#presentInstances(Iterable)
*/
@Beta
public static <T> Iterable<T> presentInstances(final Iterable<? extends Maybe<? extends T>> maybes) {
checkNotNull(maybes);
return new Iterable<T>() {
@Override
public Iterator<T> iterator() {
return new AbstractIterator<T>() {
private final Iterator<? extends Maybe<? extends T>> iterator = checkNotNull(maybes.iterator());
@Override
protected T computeNext() {
while (iterator.hasNext()) {
Maybe<? extends T> maybe = iterator.next();
if (maybe.isPresent()) { return maybe.get(); }
}
return endOfData();
}
};
}
};
}
public static class Absent<T> extends Maybe<T> {
private static final long serialVersionUID = -757170462010887057L;
private final Supplier<? extends RuntimeException> exception;
public Absent() {
this(IllegalStateExceptionSupplier.EMPTY_EXCEPTION);
}
public Absent(Supplier<? extends RuntimeException> exception) {
this.exception = exception;
}
@Override
public boolean isPresent() {
return false;
}
@Override
public boolean isNull() {
return false;
}
@Override
public T get() {
throw getException();
}
@Override
public T orThrowUnwrapped() {
throw getException();
}
public RuntimeException getException() {
return exception.get();
}
public Supplier<? extends RuntimeException> getExceptionSupplier() {
return exception;
}
public static <T> Maybe<T> changeExceptionSupplier(Maybe<T> original, final Class<? extends RuntimeException> type) {
return changeExceptionSupplier(original, new Function<AnyExceptionSupplier<?>,Supplier<? extends RuntimeException>>() {
@SuppressWarnings("unchecked")
@Override
public Supplier<? extends RuntimeException> apply(AnyExceptionSupplier<?> input) {
if (type.isInstance(input)) return (Supplier<? extends RuntimeException>) input;
return new AnyExceptionSupplier<RuntimeException>(type, input.getMessageSupplier(), input.getCause());
}
});
}
public static <T> Maybe<T> changeExceptionSupplier(Maybe<T> original, Function<AnyExceptionSupplier<?>,Supplier<? extends RuntimeException>> transform) {
if (original==null || original.isPresent()) return original;
while (original instanceof MaybeTransforming) original = ((MaybeTransforming)original).input;
if (!(original instanceof Maybe.Absent)) {
LOG.debug("Cannot replace exception supplier for "+original.getClass()+" "+original+"; ignoring");
LOG.trace("Cannot replace exception supplier for "+original.getClass()+" "+original+"; trace supplied", new Throwable("trace for irreplaceable exception supplier"));
return original;
}
final Supplier<? extends RuntimeException> supplier = ((Maybe.Absent<?>)original).getExceptionSupplier();
if (!(supplier instanceof AnyExceptionSupplier)) return original;
return Maybe.absent(transform.apply((AnyExceptionSupplier<?>)supplier));
}
}
public static class AbsentNull<T> extends Absent<T> {
private static final long serialVersionUID = 2422627709567857268L;
public AbsentNull(String message) {
super(new IllegalStateExceptionSupplier(message));
}
@Override
public boolean isNull() {
return true;
}
}
public static abstract class AbstractPresent<T> extends Maybe<T> {
private static final long serialVersionUID = -2266743425340870492L;
protected AbstractPresent() {
}
@Override
public boolean isNull() {
return get()==null;
}
@Override
public boolean isPresent() {
return true;
}
}
public static class Present<T> extends AbstractPresent<T> {
private static final long serialVersionUID = 436799990500336015L;
private final T value;
protected Present(T value) {
this.value = value;
}
@Override
public T get() {
return value;
}
}
public static class SoftlyPresent<T> extends Maybe<T> {
private static final SoftUsageTracker TRACKER = new SoftUsageTracker();
private static final long serialVersionUID = 436799990500336015L;
private final SoftReference<T> value;
private Maybe<T> defaultValue;
protected SoftlyPresent(@Nonnull T value) {
this.value = new SoftReference<T>(value);
TRACKER.track(this, this.value);
}
@Override
public T get() {
T result = value.get();
if (result!=null) return result;
if (defaultValue==null) throw new IllegalStateException("Softly present item has been GC'd");
return defaultValue.get();
}
@Override
public T orNull() {
T result = value.get();
if (result!=null) return result;
if (defaultValue==null) return null;
return defaultValue.orNull();
}
@Override
public boolean isPresent() {
return value.get()!=null || (defaultValue!=null && defaultValue.isPresent());
}
@Override
public boolean isNull() {
// null not allowed here
return false;
}
public Maybe<T> solidify() {
return Maybe.fromNullable(value.get());
}
SoftlyPresent<T> usingAfterExpiry(Maybe<T> defaultValue) {
this.defaultValue = defaultValue;
return this;
}
/** Returns the global usage tracker to determine how many references are soft */
public static SoftUsageTracker getUsageTracker() { return TRACKER; }
}
@Override
public String toString() {
return JavaClassNames.simpleClassName(this)+"["+(isPresent()?"value="+get():"")+"]";
}
@Override
public int hashCode() {
if (!isPresent()) return Objects.hashCode(31, isPresent());
return Objects.hashCode(31, get());
}
/** Two {@link Maybe} instances are equal if both present wrapping the same value,
* or if both are absent for any reason.
* <p>
* Specifically, in cases of absences, the reasons for absence are not compared.
* This could be revisited if there is compelling reason to do so, but in the main
* the cause of an absence is interesting for giving information to the user.
* Note this is different to the behaviour of {@link Optional} which says absences
* are only equal if they are the same instance.
*/
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Maybe)) return false;
Maybe<?> other = (Maybe<?>)obj;
if (!isPresent()) {
if (other.isPresent()) return false;
// could compare exceptions; see javadoc
return true;
}
if (!other.isPresent()) {
return false;
}
return Objects.equal(get(), other.get());
}
/** Finds the {@link Absent#getException()} if {@link #isAbsent()}, or else null */
public static RuntimeException getException(Maybe<?> t) {
if (t instanceof Maybe.Absent) return ((Maybe.Absent<?>)t).getException();
if (t.isPresent()) return null;
if (t instanceof MaybeTransforming) return getException( ((MaybeTransforming)t).input );
// fall back to attempting access
try {
t.get();
} catch (RuntimeException e) {
return e;
}
return new RuntimeException("Unsupported exception access on maybe type "+t.getClass()+", not present, but not in error: "+t);
}
}