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

com.aol.cyclops.trycatch.Try Maven / Gradle / Ivy

There is a newer version: 7.2.4
Show newest version

package com.aol.cyclops.trycatch;

import java.io.Closeable;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;

import lombok.AllArgsConstructor;
import lombok.val;

import com.aol.cyclops.invokedynamic.ExceptionSoftener;
import com.aol.cyclops.monad.AnyM;
import com.aol.cyclops.sequence.streamable.ToStream;
import com.aol.cyclops.value.ValueObject;

/**
 * Light weight Try Monad
 * 
 * Fail fast behaviour with more explicit declararions required compared to
 *  with scala.util.Try and javaslang.monad.Try. This is probably closer
 *  to how most Java devs currently handle exceptions.
 * 
 * Goals / features
 * 
 * Support for init block / try block  / finally block
 * Try with resources
 * Try with multiple resources
 * Does not automatically catch exceptions in combinators
 * Can target specific exception types
 * Exception types to be caught can be specified in xxxWithCatch methods
 * Handle exceptions conciously, not coding bugs
 * Fluent step builders
 * Fail fast
 * 
 * @author johnmcclean
 *
 * @param  Return type (success)
 * @param  Base Error type
 */
public interface Try extends Supplier, ValueObject, ToStream {

	/**
	 * @return This monad, wrapped as AnyM of Success
	 */
	public AnyM anyM();
	/**
	 * @return This monad, wrapped as AnyM of Failure
	 */
	public AnyM anyMFailure();
	/**
	 * @return This monad, wrapped as AnyM of Success
	 */
	public AnyM anyMSuccess();
	/**
	 * @return Successful value or will throw Throwable (X) if Failire
	 */
	public T get();
	/**
	 * Throw exception if Failure, do nothing if success
	 */
	public void throwException();
	/**
	 * @param value Return value supplied if Failure, otherwise return Success value
	 * @return Success value or supplied value
	 */
	public T orElse(T value);
	
	/**
	 * 
	 * @param value from supplied Supplier if Failure otherwise return Success value
	 * @return Success value
	 */
	public T orElseGet(Supplier value);
	
	/**
	 * @param fn Map success value from T to R. Do nothing if Failure (return this)
	 * @return New Try with mapped value (Success) or this (Failure)
	 */
	
	public  Try map(Function fn);
	
	/**
	 * @param fn FlatMap success value or Do nothing if Failure (return this)
	 * @return Try returned from FlatMap fn
	 */
	public  Try flatMap(Function> fn);
	
	
	/**
	 * @param p Convert a Success to a Failure (with a null value for Exception) if predicate does not hold.
	 *          Do nothing to a Failure
	 * @return this if Success and Predicate holds, or if Failure. New Failure if Success and Predicate fails
	 */
	public Optional filter(Predicate p);
	
	/**
	 * @param consumer Accept Exception if present (Failure)
	 * @return this
	 */
	public Try onFail(Consumer consumer);
	/**
	 * @param t Class type of match Exception against
	 * @param consumer Accept Exception if present (Failure) and if class types match
	 * @return this
	 */
	public Try onFail(Class t,Consumer consumer);
	
	/**
	 * @param fn Recovery function - map from a failure to a Success.
	 * @return new Success
	 */
	public Success recover(Function fn);
	/**
	 * flatMap recovery
	 * 
	 * @param fn Recovery FlatMap function. Map from a failure to a Success
	 * @return Success from recovery function
	 */
	public Success recoverWith(Function> fn);
	
	/**
	 * Recover if exception is of specified type
	 * @param t Type of exception to match against
	 * @param fn Recovery function
	 * @return New Success if failure and types match / otherwise this
	 */
	public Try recoverFor(Class t,Function fn);
	
	/**
	 * 
	 * FlatMap recovery function if exception is of specified type
	 * 
	 * @param t Type of exception to match against
	 * @param fn Recovery FlatMap function. Map from a failure to a Success
	 * @return Success from recovery function or this  and types match or if already Success
	 */
	public Try recoverWithFor(Class t,Function> fn);
	/**
	 * Flatten a nested Try Structure
	 * @return Lowest nested Try
	 */
	public Try flatten();
	
	/**
	 * @return Optional present if Success, Optional empty if failure
	 */
	public Optional toOptional();
	/**
	 * @return Stream with value if Sucess, Empty Stream if failure
	 */
	public Stream stream();
	/**
	 * @return Optional present if Failure (with Exception), Optional empty if Success
	 */
	public Optional toFailedOptional();
	/**
	 * @return Stream with error if Failure, Empty Stream if success
	 */
	public Stream toFailedStream();
	
	/**
	 * @return true if Success / false if Failure
	 */
	public boolean isSuccess();
	
	/**
	 * @return True if Failure / false if Success
	 */
	public boolean isFailure();
	
	/**
	 * @param consumer Accept value if Success / not called on Failure
	 */
	public void foreach(Consumer consumer);
	/**
	 * @param consumer Accept value if Failure / not called on Failure
	 */
	public void foreachFailed(Consumer consumer);
	
	/**
	 * @param consumer Accept value if Success
	 * @return this
	 */
	default Try peek(Consumer consumer){
		foreach(consumer);
		return this;
	}
	/**
	 * @param consumer Accept Exception if Failure
	 * @return this
	 */
	default Try peekFailed(Consumer consumer){
		foreachFailed(consumer);
		return this;
	}
	
	/**
	 * Return a Try that will catch specified exceptions when map / flatMap called
	 * For use with liftM / liftM2 and For Comprehensions (when Try is at the top level)
	 * 
	 * @param value Initial value
	 * @param classes Exceptions to catch during map / flatMap
	 * @return Try instance
	 */
	@SafeVarargs
	public static  Try of(T value,Class... classes){
		return new Success<>(value,classes);
	}
	
	/**
	 * Try to execute supplied Supplier and will Catch specified Excpetions or java.lang.Exception
	 * if none specified.
	 * 
	 * @param cf CheckedSupplier to attempt to execute
	 * @param classes  Exception types to catch (or java.lang.Exception if none specified)
	 * @return New Try
	 */
	@SafeVarargs
	public static  Try withCatch(CheckedSupplier cf,
						Class...classes){
		Objects.requireNonNull(cf);
		try{
			return Success.of(cf.get());
		}catch(Throwable t){
			if(classes.length==0)
				return Failure.of((X)t);
			val error = Stream.of(classes).filter(c -> c.isAssignableFrom(t.getClass())).findFirst();
			if(error.isPresent())
				return Failure.of((X)t);
			else
				throw ExceptionSoftener.throwSoftenedException(t);
		}
		
	}
	/**
	 * Try to execute supplied Runnable and will Catch specified Excpetions or java.lang.Exception
	 * if none specified.
	 * 
	 * @param cf CheckedRunnable to attempt to execute
	 * @param classes  Exception types to catch (or java.lang.Exception if none specified)
	 * @return New Try
	 */
	@SafeVarargs
	public static   Try runWithCatch(CheckedRunnable cf,Class...classes){
		Objects.requireNonNull(cf);
		try{
			cf.run();
			return Success.of(null);
		}catch(Throwable t){
			t.printStackTrace();
			if(classes.length==0)
				return Failure.of((X)t);
			val error = Stream.of(classes).filter(c -> c.isAssignableFrom(t.getClass())).findFirst();
			if(error.isPresent())
				return Failure.of((X)t);
			else
				throw ExceptionSoftener.throwSoftenedException(t);
		}
		
	}
	/**
	 * Fluent step builder for Try / Catch / Finally and Try with resources equivalents.
	 * Start with Exception types to catch.
	 * 
	 * @param classes Exception types to catch
	 * @return Next step in the fluent Step Builder
	 */
	@SafeVarargs
	public static  Init catchExceptions(Class...classes){
		return new MyInit((Class[])classes);
	}
	
	
	
	@AllArgsConstructor
	static class MyInit implements Init{
		private final Class[] classes;
		/* 
		 *	@param input
		 *	@return
		 * @see com.aol.cyclops.trycatch.Try.Init#init(com.aol.cyclops.trycatch.Try.CheckedSupplier)
		 */
		@Override
		public  TryCatch init(CheckedSupplier input) {
			return new MyTryCatch(classes,input);
		}
		
		@Override
		public Try run(CheckedRunnable input) {
			return runWithCatch(input,classes);
		}
		@Override
		public  Try tryThis(CheckedSupplier input) {
			return withCatch(input,classes);
		}
		
	}
	@AllArgsConstructor
	static class MyTryCatch implements TryCatch{
		private final Class[] classes;
		private final CheckedSupplier inputSupplier;

		@Override
		public  AndFinally tryThis(CheckedFunction catchBlock) {
			return new MyFinallyBlock<>(classes,inputSupplier,catchBlock);
		}
		@Override
		public  Try tryWithResources(CheckedFunction catchBlock) {
			return new MyFinallyBlock<>(classes,inputSupplier,catchBlock).close();
		}
		
	}
	@AllArgsConstructor
	public static class MyFinallyBlock implements AndFinally{
		private final Class[] classes;
		private final CheckedSupplier inputSupplier;
		private final CheckedFunction catchBlock;
		
		private void invokeClose(Object in) {
			if(in instanceof Closeable)
				invokeCloseableClose((Closeable)in);
			else if( in instanceof AutoCloseable)
				invokeAutocloseableClose((AutoCloseable)in);
			else if(in instanceof Iterable)
				invokeClose((Iterable)in);
			else
				_invokeClose(in);
		}
		private void invokeClose(Iterable in){
			for(Object next : in)
				invokeClose(next);
			
		
	}
		private void invokeCloseableClose(Closeable in){
			
			Try.runWithCatch(()->in.close());
		
	}
		private void invokeAutocloseableClose(AutoCloseable in){
			
			Try.runWithCatch(()->in.close());
		
	}
		private void _invokeClose(Object in){
			
				Try.withCatch(()->in.getClass().getMethod("close")).filter(m->m!=null)
						.flatMap(m->Try.withCatch(()->m.invoke(in))
											.filter(o->o!=null));
			
		}
		public Try close(){
			
			return andFinally(in -> {System.out.println("hello!!");invokeClose(in);} );
		}
		
		@Override
		public Try andFinally(final CheckedConsumer finallyBlock) {
			
				
							
				Try input =  Try.withCatch(() ->inputSupplier.get(),classes);
				Try result = null;
				try{
					result  =input.flatMap(in -> withCatch(()->catchBlock.apply(in),classes) );
				
				}finally{
					Try finalResult  = result.flatMap(i-> Try.runWithCatch(() ->finallyBlock.accept(inputSupplier.get()),classes));
					if(finalResult instanceof Failure)
						return finalResult;
				
				}
			return result;
		}
		
	}
	
	public static interface Init{
		/**
		 * Initialise a try / catch / finally block
		 * Define the variables to be used within the block.
		 * A Tuple or Iterable can be returned to defined multiple values.
		 * Closeables (either individually or within an iterable) will be closed
		 * via tryWithResources.
		 * 
		 * 
		 * 
		 * Try.catchExceptions(FileNotFoundException.class,IOException.class)
		 *		   .init(()->new BufferedReader(new FileReader("file.txt")))
	     *		   .tryWithResources(this::read);
		 * 
		 * 
* * or * *
		 * 
		 * Try t2 = Try.catchExceptions(FileNotFoundException.class,IOException.class)
		 *		   .init(()->Tuple.tuple(new BufferedReader(new FileReader("file.txt")),new FileReader("hello")))
		 *		   .tryWithResources(this::read2);
	     * 
	     * private String read2(Tuple2<BufferedReader,FileReader> res) throws IOException{
		 * String line = res.v1.readLine();
		 * 
		 * 
* * @param input Supplier that provides input values to be used in the Try / Catch * @return */ TryCatch init(CheckedSupplier input); /** * Run the supplied CheckedRunnable and trap any Exceptions * Return type is Void * * @param input CheckedRunnable * @return Try that traps any errors (no return type) */ Try run(CheckedRunnable input); /** * Run the supplied CheckedSupplier and trap the return value or an Exception * inside a Try * * @param input CheckedSupplier to run * @return new Try */ Try tryThis(CheckedSupplier input); } public static interface TryCatch { /** * Will execute and run the CheckedFunction supplied and will automatically * safely close any Closeables supplied during init (either individually or inside an iterable) * * @param catchBlock CheckedFunction to Try * @return New Try capturing return data or Exception */ Try tryWithResources(CheckedFunction catchBlock); /** * Build another stage in try / catch / finally block * This defines the CheckedFunction that will be run in the main body of the catch block * Next step can define the finally block * * @param catchBlock To Try * @return Next stage in the fluent step builder (finally block) */ AndFinally tryThis(CheckedFunction catchBlock); } public static interface AndFinally { /** * Define the finally block and execute the Try * * @param finallyBlock to execute * @return New Try capturing return data or Exception */ Try andFinally(CheckedConsumer finallyBlock); /** * Create a finally block that auto-closes any Closeables specified during init * including those inside an Iterable * * @return New Try capturing return data or Exception */ Try close(); } public static interface CheckedFunction{ public R apply(T t) throws X; } public static interface CheckedSupplier{ public T get() throws X; } public static interface CheckedConsumer{ public void accept(T t) throws X; } public static interface CheckedRunnable{ public void run() throws X; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy