001/* 002 * Copyright 2016 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.core.util; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.LinkedHashMap; 021import java.util.List; 022import java.util.Map; 023import java.util.Objects; 024import java.util.function.BinaryOperator; 025import java.util.function.Consumer; 026import java.util.function.Function; 027import java.util.function.Predicate; 028import java.util.stream.Collector; 029 030/** 031 * Helper for lambda expressions. 032 */ 033public final class LambdaUtils 034{ 035 private LambdaUtils() 036 { 037 // empty constructor 038 } 039 040 /** 041 * Function allowed to throw checked {@link Exception}. <br> 042 * It allows to build one-line lambda for methods throwing checked exceptions. 043 * @param <T> the type of the input to the function. 044 * @param <R> the type of the result of the function. 045 */ 046 @FunctionalInterface 047 public interface ThrowingFunction<T, R> 048 { 049 /** 050 * Applies this function to the given argument. 051 * @param t the function argument 052 * @return the function result 053 * @throws Exception if something wrong occurs. 054 */ 055 R apply(T t) throws Exception; 056 } 057 058 /** 059 * Wraps a {@link Function} by catching its {@link Exception} and rethrowing them as {@link LambdaException}. 060 * @param function the {@link Function} to wrap. 061 * @param <T> The type of input to the function 062 * @param <R> The type of result of the function 063 * @return the wrapped {@link Function}. 064 */ 065 public static <T, R> Function<T, R> wrap(ThrowingFunction<T, R> function) 066 { 067 return value -> 068 { 069 try 070 { 071 return function.apply(value); 072 } 073 catch (Exception e) 074 { 075 if (e instanceof RuntimeException) 076 { 077 throw (RuntimeException) e; 078 } 079 080 throw new LambdaException(e); 081 } 082 }; 083 } 084 085 /** 086 * Predicate allowed to throw checked {@link Exception}. <br> 087 * It allows to build one-line lambda for methods throwing checked exceptions. 088 * @param <T> the type of the input to the predicate. 089 */ 090 @FunctionalInterface 091 public interface ThrowingPredicate<T> 092 { 093 /** 094 * Evaluates this predicate on the given argument. 095 * @param t the input argument 096 * @return {@code true} if the input argument matches the predicate, 097 * otherwise {@code false} 098 * @throws Exception if something wrong occurs. 099 */ 100 boolean test(T t) throws Exception; 101 } 102 103 /** 104 * Wraps a {@link Predicate} by catching its {@link Exception} and rethrowing them as {@link LambdaException}. 105 * @param predicate the {@link Predicate} to wrap. 106 * @param <T> the type of the input to the predicate 107 * @return the wrapped {@link Predicate}. 108 */ 109 public static <T> Predicate<T> wrapPredicate(ThrowingPredicate<T> predicate) 110 { 111 return LambdaUtils.wrap(predicate::test)::apply; 112 113 } 114 115 /** 116 * Consumer allowed to throw checked {@link Exception}. <br> 117 * It allows to build one-line lambda for methods throwing checked exceptions. 118 * @param <T> the type of the input to the operation 119 */ 120 @FunctionalInterface 121 public interface ThrowingConsumer<T> extends ThrowingConsumerExceptionAware<T, Exception> 122 { 123 // Everything is in ThrowingConsumerExceptionAware 124 } 125 126 /** 127 * Consumer allowed to throw checked {@link Exception}. <br> 128 * It allows to build one-line lambda for methods throwing checked exceptions. 129 * @param <T> the type of the input to the operation 130 * @param <E> the type of the exception. 131 */ 132 @FunctionalInterface 133 public interface ThrowingConsumerExceptionAware<T, E extends Exception> 134 { 135 /** 136 * Performs this operation on the given argument. 137 * @param t the input argument 138 * @throws Exception if something wrong occurs. 139 */ 140 void accept(T t) throws E; 141 } 142 143 /** 144 * Wraps a {@link Consumer} by catching its {@link Exception} and rethrowing them as {@link LambdaException}. 145 * @param consumer the {@link Consumer} to wrap. 146 * @param <T> The type of input to the operation 147 * @return the wrapped {@link Consumer}. 148 */ 149 public static <T> Consumer<T> wrapConsumer(ThrowingConsumer<T> consumer) 150 { 151 return value -> 152 { 153 try 154 { 155 consumer.accept(value); 156 } 157 catch (Exception e) 158 { 159 if (e instanceof RuntimeException) 160 { 161 throw (RuntimeException) e; 162 } 163 164 throw new LambdaException(e); 165 } 166 }; 167 } 168 169 /** 170 * Some useful {@link Collector collectors} 171 */ 172 public static class Collectors 173 { 174 /** 175 * Returns a {@link Collector} that accumulates elements into a 176 * {@link LinkedHashMap} whose keys and values are the result of applying the provided 177 * mapping functions to the input elements. 178 * <br> 179 * <br>This is the same as {@link java.util.stream.Collectors#toMap(Function, Function)}, 180 * but elements are collected into a {@link LinkedHashMap} instead of a {@link HashMap} 181 * 182 * @param <T> the type of the input elements 183 * @param <K> the output type of the key mapping function 184 * @param <U> the output type of the value mapping function 185 * @param keyMapper a mapping function to produce keys 186 * @param valueMapper a mapping function to produce values 187 * @return a {@code Collector} which collects elements into a {@link LinkedHashMap} 188 * whose keys and values are the result of applying mapping functions to 189 * the input elements 190 */ 191 public static <T, K, U> Collector<T, ?, Map<K, U>> toLinkedHashMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) 192 { 193 return java.util.stream.Collectors.toMap(keyMapper, valueMapper, _throwingMerger(), LinkedHashMap::new); 194 } 195 196 // copied from java.util.stream.Collectors.throwingMerger() 197 private static <T> BinaryOperator<T> _throwingMerger() 198 { 199 return (u, v) -> 200 { 201 throw new IllegalStateException(String.format("Duplicate key %s", u)); 202 }; 203 } 204 205 /** 206 * A convenient method for creating a {@link Collector} which accumulates elements into a {@link List}, 207 * this list being transformed by the provided finisher into the final result. 208 * @param <T> the type of the input elements 209 * @param <R> the final result type 210 * @param finisher The finisher function for the new collector 211 * @return the new collector 212 */ 213 public static <T, R> Collector<T, List<T>, R> withListAccumulation(Function<List<T>, R> finisher) 214 { 215 return Collector.of( 216 ArrayList<T>::new, 217 (res, q) -> res.add(q), 218 (res1, res2) -> 219 { 220 res1.addAll(res2); 221 return res1; 222 }, 223 finisher); 224 } 225 } 226 227 /** 228 * Some useful {@link BiPredicate biPredicates} 229 * @param <T> the type of arguments to the specified bipredicate 230 * @param <U> the type of arguments to the specified bipredicate 231 */ 232 public static class BiPredicate<T, U> 233 { 234 /** 235 * Returns a bipredicate that is the negation of the supplied bipredicate. 236 * @param <T> the type of arguments to the specified bipredicate 237 * @param <U> the type of arguments to the specified bipredicate 238 * @param target bipredicate to negate 239 * @return a bipredicate that negates the results of the supplied bipredicate 240 */ 241 @SuppressWarnings("unchecked") 242 public static <T, U> java.util.function.BiPredicate<T, U> not(java.util.function.BiPredicate<? super T, ? super U> target) 243 { 244 Objects.requireNonNull(target); 245 return (java.util.function.BiPredicate<T, U>) target.negate(); 246 } 247 } 248 249 /** 250 * Runtime exception wrapping the exception thrown from the lambda 251 */ 252 public static class LambdaException extends RuntimeException 253 { 254 /** Constructs a new runtime exception with the specified cause and a 255 * detail message of {@code (cause==null ? null : cause.toString())} 256 * (which typically contains the class and detail message of 257 * {@code cause}). This constructor is useful for runtime exceptions 258 * that are little more than wrappers for other throwables. 259 * 260 * @param cause the cause (which is saved for later retrieval by the 261 * {@link #getCause()} method). (A {@code null} value is 262 * permitted, and indicates that the cause is nonexistent or 263 * unknown.) 264 */ 265 public LambdaException(Throwable cause) 266 { 267 super(cause); 268 } 269 } 270}