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> 122 { 123 /** 124 * Performs this operation on the given argument. 125 * @param t the input argument 126 * @throws Exception if something wrong occurs. 127 */ 128 void accept(T t) throws Exception; 129 } 130 131 /** 132 * Wraps a {@link Consumer} by catching its {@link Exception} and rethrowing them as {@link LambdaException}. 133 * @param consumer the {@link Consumer} to wrap. 134 * @param <T> The type of input to the operation 135 * @return the wrapped {@link Consumer}. 136 */ 137 public static <T> Consumer<T> wrapConsumer(ThrowingConsumer<T> consumer) 138 { 139 return value -> 140 { 141 try 142 { 143 consumer.accept(value); 144 } 145 catch (Exception e) 146 { 147 if (e instanceof RuntimeException) 148 { 149 throw (RuntimeException) e; 150 } 151 152 throw new LambdaException(e); 153 } 154 }; 155 } 156 157 /** 158 * Some useful {@link Collector collectors} 159 */ 160 public static class Collectors 161 { 162 /** 163 * Returns a {@link Collector} that accumulates elements into a 164 * {@link LinkedHashMap} whose keys and values are the result of applying the provided 165 * mapping functions to the input elements. 166 * <br> 167 * <br>This is the same as {@link java.util.stream.Collectors#toMap(Function, Function)}, 168 * but elements are collected into a {@link LinkedHashMap} instead of a {@link HashMap} 169 * 170 * @param <T> the type of the input elements 171 * @param <K> the output type of the key mapping function 172 * @param <U> the output type of the value mapping function 173 * @param keyMapper a mapping function to produce keys 174 * @param valueMapper a mapping function to produce values 175 * @return a {@code Collector} which collects elements into a {@link LinkedHashMap} 176 * whose keys and values are the result of applying mapping functions to 177 * the input elements 178 */ 179 public static <T, K, U> Collector<T, ?, Map<K, U>> toLinkedHashMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) 180 { 181 return java.util.stream.Collectors.toMap(keyMapper, valueMapper, _throwingMerger(), LinkedHashMap::new); 182 } 183 184 // copied from java.util.stream.Collectors.throwingMerger() 185 private static <T> BinaryOperator<T> _throwingMerger() 186 { 187 return (u, v) -> 188 { 189 throw new IllegalStateException(String.format("Duplicate key %s", u)); 190 }; 191 } 192 193 /** 194 * A convenient method for creating a {@link Collector} which accumulates elements into a {@link List}, 195 * this list being transformed by the provided finisher into the final result. 196 * @param <T> the type of the input elements 197 * @param <R> the final result type 198 * @param finisher The finisher function for the new collector 199 * @return the new collector 200 */ 201 public static <T, R> Collector<T, List<T>, R> withListAccumulation(Function<List<T>, R> finisher) 202 { 203 return Collector.of( 204 ArrayList<T>::new, 205 (res, q) -> res.add(q), 206 (res1, res2) -> 207 { 208 res1.addAll(res2); 209 return res1; 210 }, 211 finisher); 212 } 213 } 214 215 /** 216 * Some useful {@link BiPredicate biPredicates} 217 * @param <T> the type of arguments to the specified bipredicate 218 * @param <U> the type of arguments to the specified bipredicate 219 */ 220 public static class BiPredicate<T, U> 221 { 222 /** 223 * Returns a bipredicate that is the negation of the supplied bipredicate. 224 * @param <T> the type of arguments to the specified bipredicate 225 * @param <U> the type of arguments to the specified bipredicate 226 * @param target bipredicate to negate 227 * @return a bipredicate that negates the results of the supplied bipredicate 228 */ 229 @SuppressWarnings("unchecked") 230 public static <T, U> java.util.function.BiPredicate<T, U> not(java.util.function.BiPredicate<? super T, ? super U> target) 231 { 232 Objects.requireNonNull(target); 233 return (java.util.function.BiPredicate<T, U>) target.negate(); 234 } 235 } 236 237 /** 238 * Runtime exception wrapping the exception thrown from the lambda 239 */ 240 public static class LambdaException extends RuntimeException 241 { 242 /** Constructs a new runtime exception with the specified cause and a 243 * detail message of {@code (cause==null ? null : cause.toString())} 244 * (which typically contains the class and detail message of 245 * {@code cause}). This constructor is useful for runtime exceptions 246 * that are little more than wrappers for other throwables. 247 * 248 * @param cause the cause (which is saved for later retrieval by the 249 * {@link #getCause()} method). (A {@code null} value is 250 * permitted, and indicates that the cause is nonexistent or 251 * unknown.) 252 */ 253 public LambdaException(Throwable cause) 254 { 255 super(cause); 256 } 257 } 258}