/************************************************************************* * Copyright 2009-2016 Eucalyptus Systems, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need * additional information or have any questions. ************************************************************************/ package com.eucalyptus.util; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.google.common.base.Enums; import com.google.common.base.Function; import com.google.common.base.Optional; import javaslang.Tuple2; import javaslang.collection.Stream; import javaslang.Tuple; /** * Functional utility methods */ public class FUtils { /** * A function that applies all of the given functions to the input. * * @param mappers The mapping functions to be applied. * @param <R> The result item type. * @param <T> The input type * @return The function */ public static <R,T> CompatFunction<T,Iterable<R>> applyAll( final Iterable<Function<? super T,? extends R>> mappers ) { return t -> Stream.ofAll( mappers ).map( mapper -> mapper.apply( t ) ); } /** * Chain functions, useful for method reference composition. */ public static <I,P,R> java.util.function.Function<P,R> chain( final java.util.function.Function<? super P, ? extends I> f1, final java.util.function.Function<? super I, R> f2 ) { return f2.compose( f1 ); } /** * Partially apply the function using the given parameter. * * @return Callable type thunk */ public static <R,P> Callable<R> cpartial( final Function<? super P, ? extends R> function, final P param ) { return new Callable<R>( ) { @Override public R call( ) throws Exception { return function.apply( param ); } }; } /** * Flatten a nested optional. * * @param option The optional to flatten * @param <T> The resulting optional type * @return The optional */ @Nonnull public static <T> Optional<T> flatten( @Nullable final Optional<? extends Optional<T>> option ) { if ( option != null && option.isPresent( ) ) { return option.get( ); } return Optional.absent( ); } /** * Replacement for the removed guava Enums#valueOfFunction * * @param enumClass * @param <T> * @return */ public static <T extends Enum<T>> CompatFunction<String,T> valueOfFunction( final Class<T> enumClass ) { return new CompatFunction<String,T>( ){ @Nullable @Override public T apply( @Nullable final String value ) { return value == null ? null : Enums.getIfPresent( enumClass, value ).orNull( ); } }; } /** * Function that calls the callback and returns the parameter. * * @param callback The Callback to call * @param <T> The Callback type * @return A function wrapping the callback */ public static <T> CompatFunction<T,T> function( @Nonnull final Callback<T> callback ) { return new CompatFunction<T,T>( ) { @Nullable @Override public T apply( @Nullable final T t ) { callback.fire( t ); return t; } }; } /** * Catch any exception thrown by the function and return an either. * * @param <T> The function parameter type * @param <R> The function return type * @param function The function to call * * @return The exception handling function */ public static <T,R> CompatFunction<T,Either<Throwable,R>> eitherThrowable( final java.util.function.Function<T,R> function ) { return t -> { try { return Either.right( function.apply( t ) ); } catch ( final Throwable thrown ) { return Either.left( thrown ); } }; } /** * Catch any exception thrown by the function and return an option. * * The returned option is absent in case of exceptions or null return. * * @param <T> The function parameter type * @param <R> The function return type * @param function The function to call * * @return The exception and null handling function */ public static <T,R> CompatFunction<T,Optional<R>> optional( final java.util.function.Function<T,R> function ) { return t -> { try { return Optional.fromNullable( function.apply( t ) ); } catch ( final Throwable thrown ) { return Optional.absent( ); } }; } /** * Tuplize the result of a function with its parameter * * @param <T> The function parameter type * @param <R> The function return type * @param function The function to tuplize * * @return The exception handling function */ public static <T,R> CompatFunction<T, Tuple2<T,R>> tuple( final java.util.function.Function<T,R> function ) { return t -> Tuple.of( t, function.apply( t ) ); } /** * Memoize the last successful invocation with non-null parameter/result. * * @param function The function to memoize * @param <F> The parameter type * @param <T> The result type * @return A function that memoizes the last result */ public static <F,T> CompatFunction<F,T> memoizeLast( @Nonnull final Function<F,T> function ) { return new CompatFunction<F, T>( ) { private final AtomicReference<Pair<F,T>> cached = new AtomicReference<>( ); @Nullable @Override public T apply( @Nullable final F f ) { final Pair<F,T> cachedResult = cached.get( ); if ( cachedResult != null && cachedResult.getLeft( ).equals( f ) ) { return cachedResult.getRight( ); } else { final T result = function.apply( f ); if ( f != null && result != null ) { cached.compareAndSet( cachedResult, Pair.pair( f, result ) ); } return result; } } }; } /** * Memoize the last successful invocation with non-null parameter/result. * * Will only call function with one thread at a time. * * @param function The function to memoize * @param <F> The parameter type * @param <T> The result type * @return A function that memoizes the last result */ public static <F,T> CompatFunction<F,T> memoizeLastSync( @Nonnull final Function<F,T> function ) { return new CompatFunction<F, T>( ) { private final Lock lock = new ReentrantLock( ); private final AtomicReference<Pair<F,T>> cached = new AtomicReference<>( ); @Nullable @Override public T apply( @Nullable final F f ) { final Pair<F,T> cachedResult = cached.get( ); if ( cachedResult != null && cachedResult.getLeft( ).equals( f ) ) { return cachedResult.getRight( ); } else try ( final LockResource lockResource = LockResource.lock( lock ) ) { final Pair<F,T> syncCachedResult = cached.get( ); if ( syncCachedResult != null && syncCachedResult.getLeft( ).equals( f ) ) { return syncCachedResult.getRight( ); } else { final T result = function.apply( f ); if ( f != null && result != null ) { cached.compareAndSet( cachedResult, Pair.pair( f, result ) ); } return result; } } } }; } }