All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.isis.commons.functional.Try Maven / Gradle / Ivy

/*
 *  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.isis.commons.functional;

import java.io.Serializable;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.UnaryOperator;

import org.springframework.lang.Nullable;

import org.apache.isis.commons.internal.exceptions._Exceptions;

import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;

/**
 * The {@link Try} type represents a value of one of two possible types (a disjoint union)
 * of {@link Success} or {@link Failure}.
 * 

* Factory methods {@link Try#success(Object)} and {@link Try#failure(Throwable)} * correspond to the two possible values. *

* Follows the Railway Pattern, that is, once failed, stays failed. * @see Railway * * @since 2.0 {@index} */ public interface Try { // -- FACTORIES public static Try call(final @NonNull Callable callable) { try { return success(callable.call()); } catch (Throwable e) { return failure(e); } } public static Try run(final @NonNull ThrowingRunnable runnable) { try { runnable.run(); return success(null); } catch (Throwable e) { return failure(e); } } public static Success success(final @Nullable T value) { return new Success<>(value); } public static Failure failure(final @NonNull Throwable throwable) { return new Failure(throwable); } // -- PREDICATES boolean isSuccess(); boolean isFailure(); // -- ACCESSORS /** * Optionally returns the contained {@code value} based on presence, * that is, if this is a {@link Success} and the value is not {@code null}. */ Optional getValue(); /** * Optionally returns the contained {@code failure} based on presence, * that is, if this is a {@link Failure}. */ Optional getFailure(); // -- PEEKING /** * Peeks into the {@code value} if this is a {@link Success}. */ Try ifSuccess(final @NonNull Consumer> valueConsumer); /** * Peeks into the {@code failure} if this is a {@link Failure}. */ Try ifFailure(final @NonNull Consumer exceptionConsumer); // -- FAIL EARLY /** Throws the contained failure if any. */ Try ifFailureFail(); /** Throws {@link NoSuchElementException} if {@code value} is {@code null}. */ Try ifAbsentFail(); // -- MAPPING /** * Maps this {@link Try} to another if this is a {@link Success}. * Otherwise if this is a {@link Failure} acts as identity operator. */ Try mapSuccess(final @NonNull Function successMapper); /** * Maps this {@link Try} to another if its a {@link Failure}. * Otherwise if this is a {@link Success} acts as identity operator. */ Try mapFailure(final @NonNull UnaryOperator failureMapper); /** * Maps this {@link Try} to {@link Failure} if this is a {@link Success} with an empty {@code value}. * Otherwise acts as identity operator. */ Try mapEmptyToFailure(); /** * Maps this {@link Try} to {@link Either} * using according mapping function {@code successMapper} or {@code failureMapper}. * @apiNote It is a common functional programming convention, to map the success value right. */ Either map( final @NonNull Function failureMapper, final @NonNull Function, R> successMapper); // -- TERMINATE /** * Either consumes the success or the failure. * @apiNote Order of arguments conforms to {@link #map(Function, Function)} */ void accept( final @NonNull Consumer failureConsumer, final @NonNull Consumer> successConsumer); // -- FOLDING /** * Maps the contained {@code value} or {@code failure} to a new value of type {@code R} * using according mapping function {@code successMapper} or {@code failureMapper}. * @apiNote Order of arguments conforms to {@link #map(Function, Function)} */ R fold( final @NonNull Function failureMapper, final @NonNull Function, R> successMapper); // -- CONCATENATION /** * If this is a {@link Success}, maps it to a new {@link Try} based on given {@link Callable}. * Otherwise if its a {@link Failure} acts as identity operator. */ Try thenCall(final @NonNull Callable callable); /** * If this is a {@link Success}, maps it to new {@link Try} based on given {@link ThrowingRunnable}. * Otherwise if its a {@link Failure} acts as identity operator. */ Try thenRun(final @NonNull ThrowingRunnable runnable); // -- SUCCESS @lombok.Value @RequiredArgsConstructor final class Success implements Try, Serializable { private static final long serialVersionUID = 1L; private final @Nullable T value; @Override public boolean isSuccess() { return true; } @Override public boolean isFailure() { return false; } @Override public Optional getValue() { return Optional.ofNullable(value); } @Override public Optional getFailure() { return Optional.empty(); } @Override public Success ifSuccess(final @NonNull Consumer> valueConsumer) { valueConsumer.accept(getValue()); return this; } @Override public Success ifFailure(final @NonNull Consumer exceptionConsumer) { return this; } @Override public Success ifFailureFail() { return this; } @Override public Success ifAbsentFail() { if(value==null) throw _Exceptions.noSuchElement(); return this; } @Override public Try mapSuccess(final @NonNull Function successMapper){ return Try.call(()->successMapper.apply(value)); } @Override public Success mapFailure(final @NonNull UnaryOperator failureMapper){ return this; } @Override public Try mapEmptyToFailure() { return value!=null ? this : Try.failure(_Exceptions.noSuchElement()); } @Override public Try thenCall(final @NonNull Callable callable) { return Try.call(callable); } @Override public Try thenRun(final @NonNull ThrowingRunnable runnable) { return Try.run(runnable); } @Override public void accept( final @NonNull Consumer failureConsumer, final @NonNull Consumer> successConsumer) { successConsumer.accept(getValue()); } @Override public R fold( final @NonNull Function failureMapper, final @NonNull Function, R> successMapper) { return successMapper.apply(getValue()); } @Override public Either map( final @NonNull Function failureMapper, final @NonNull Function, R> successMapper) { return Either.right(successMapper.apply(getValue())); } } // -- FAILURE @lombok.Value @RequiredArgsConstructor final class Failure implements Try, Serializable { private static final long serialVersionUID = 1L; private final @NonNull Throwable throwable; @Override public boolean isSuccess() { return false; } @Override public boolean isFailure() { return true; } @Override public Optional getValue() { return Optional.empty(); } @Override public Optional getFailure() { return Optional.of(throwable); } @Override public Failure ifSuccess(final @NonNull Consumer> valueConsumer) { return this; } @Override public Failure ifFailure(final @NonNull Consumer exceptionConsumer) { exceptionConsumer.accept(throwable); return this; } @Override @SneakyThrows public Failure ifFailureFail() { throw throwable; } @Override @SneakyThrows public Failure ifAbsentFail() { throw _Exceptions.noSuchElement(); } @Override public Failure mapSuccess(final @NonNull Function successMapper){ return new Failure<>(throwable); } @Override public Failure mapFailure(final @NonNull UnaryOperator failureMapper){ try { return new Failure<>(failureMapper.apply(throwable)); } catch (Throwable e) { return failure(e); } } @Override public Try mapEmptyToFailure() { return this; } @Override public Failure thenCall(final @NonNull Callable callable) { return new Failure<>(throwable); } @Override public Try thenRun(final @NonNull ThrowingRunnable runnable) { return new Failure<>(throwable); } @Override public void accept( final @NonNull Consumer failureConsumer, final @NonNull Consumer> successConsumer) { failureConsumer.accept(throwable); } @Override public R fold( final @NonNull Function failureMapper, final @NonNull Function, R> successMapper) { return failureMapper.apply(throwable); } @Override public Either map( final @NonNull Function failureMapper, final @NonNull Function, R> successMapper) { return Either.left(failureMapper.apply(throwable)); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy