/*
* Copyright 2010, Maarten Billemont
*
* Licensed 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 com.lyndir.omicron.api.util;
import com.google.common.base.Preconditions;
import java.util.Objects;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* @author lhunath, 2013-08-10
*/
public abstract class Maybe<T> {
public static <T> Maybe<T> empty() {
return new Empty<>();
}
public static <T> Maybe<T> unknown() {
return new Unknown<>();
}
/**
* @return {@link Presence#EMPTY} if reference is null, otherwise {@link Presence#PRESENT}.
*/
public static <T> Maybe<T> ofNullable(@Nullable final T reference) {
return reference == null? Maybe.<T>empty(): new Present<>( reference );
}
/**
* @return {@link Presence#EMPTY} if reference is {@link Optional#empty()}, otherwise {@link Presence#PRESENT}.
*/
public static <T> Maybe<T> ofOptional(final Optional<T> reference) {
return reference.isPresent()? new Present<>( reference.get() ): Maybe.<T>empty();
}
/**
* @return Always {@link Presence#PRESENT}.
*/
public static <T> Maybe<T> of(@Nonnull final T reference) {
return new Present<>( reference );
}
/**
* @return The availability of the reference.
*/
public abstract Presence presence();
/**
* @return {@code true} if the reference is present.
*/
public boolean isPresent() {
return presence() == Presence.PRESENT;
}
/**
* @return {@code true} if the reference is unknown.
*/
public boolean isUnknown() {
return presence() == Presence.UNKNOWN;
}
/**
* @return {@code true} if the reference is empty.
*/
public boolean isEmpty() {
return presence() == Presence.EMPTY;
}
/**
* @return {@code true} if the reference is either present or empty (ie. not unknown).
*/
public boolean isKnown() {
return isPresent() || isEmpty();
}
@Nonnull
public abstract T get();
@Nullable
public T orNull() {
if (presence() == Presence.PRESENT)
return get();
return null;
}
public abstract String toString();
public enum Presence {
EMPTY, UNKNOWN, PRESENT
}
private static class Empty<T> extends Maybe<T> {
@Override
public int hashCode() {
return Objects.hash( presence() );
}
@Override
public boolean equals(final Object obj) {
return obj instanceof Empty;
}
@Override
public Presence presence() {
return Presence.EMPTY;
}
@Nonnull
@Override
public T get() {
throw new IllegalStateException( "Cannot get() an empty reference." );
}
@Override
public String toString() {
return "<empty>";
}
}
private static class Unknown<T> extends Maybe<T> {
@Override
public int hashCode() {
return Objects.hash( presence() );
}
@Override
public boolean equals(final Object obj) {
return obj instanceof Unknown;
}
@Override
public Presence presence() {
return Presence.UNKNOWN;
}
@Nonnull
@Override
public T get() {
throw new IllegalStateException( "Cannot get() an unknown reference." );
}
@Override
public String toString() {
return "<unknown>";
}
}
private static class Present<T> extends Maybe<T> {
private final T reference;
private Present(final T reference) {
this.reference = Preconditions.checkNotNull( reference, "Missing object for present reference." );
}
@Override
public int hashCode() {
return Objects.hash( presence(), reference );
}
@Override
public boolean equals(final Object obj) {
return obj instanceof Present && Objects.equals( reference, ((Present<?>) obj).reference );
}
@Override
public Presence presence() {
return Presence.PRESENT;
}
@Nonnull
@Override
public T get() {
return reference;
}
@Override
public String toString() {
return String.format( "<present: %s>", reference.toString() );
}
}
}