001/* 002 * MIT License 003 * 004 * Copyright (c) 2023 Michael Cowan 005 * 006 * Permission is hereby granted, free of charge, to any person obtaining a copy 007 * of this software and associated documentation files (the "Software"), to deal 008 * in the Software without restriction, including without limitation the rights 009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 010 * copies of the Software, and to permit persons to whom the Software is 011 * furnished to do so, subject to the following conditions: 012 * 013 * The above copyright notice and this permission notice shall be included in all 014 * copies or substantial portions of the Software. 015 * 016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 022 * SOFTWARE. 023 */ 024 025package io.blt.util; 026 027import io.blt.util.functional.ThrowingFunction; 028import java.util.HashMap; 029import java.util.Map; 030 031import static io.blt.util.Obj.newInstanceOf; 032 033/** 034 * Static utility methods for operating on implementations of {@code Collection} and {@code Map} i.e. Containers. 035 * <p> 036 * For methods that return a modification of a passed container, the result will be of the same type if possible. 037 * This is accomplished using {@link Obj#newInstanceOf(Object)} and its limitations apply. 038 * </p> 039 */ 040public final class Ctr { 041 042 private Ctr() { 043 throw new IllegalAccessError("Utility class should be accessed statically and never constructed"); 044 } 045 046 /** 047 * Returns a new {@link Map} containing the entries of {@code source} with {@code transform} applied to the values. 048 * <p> 049 * If possible, the result is of the same type as the passed {@code source} map. 050 * </p> 051 * e.g. 052 * <pre>{@code 053 * var scores = Map.of("Louis", 95, "Greg", 92, "Mike", 71, "Phil", 86); 054 * var grades = Ctr.transformValues(scores, score -> { 055 * if (score >= 90) { 056 * return "A"; 057 * } else if (score >= 80) { 058 * return "B"; 059 * } else if (score >= 70) { 060 * return "C"; 061 * } else if (score >= 60) { 062 * return "D"; 063 * } else { 064 * return "F"; 065 * } 066 * }); 067 * // grades = Map.of("Louis", "A", "Greg", "A", "Mike", "C", "Phil", "B") 068 * }</pre> 069 * 070 * @param source {@link Map} whose values should be transformed 071 * @param transform value transformation function 072 * @param <K> {@code map} key type 073 * @param <V> {@code map} value type 074 * @param <R> returned map value type 075 * @param <E> type of {@code transform} throwable 076 * @return a new {@link Map} containing the entries of {@code source} with {@code transform} applied to the values 077 * @see Obj#newInstanceOf(Object) 078 */ 079 @SuppressWarnings("unchecked") 080 public static <K, V, R, E extends Throwable> Map<K, R> transformValues( 081 Map<K, V> source, ThrowingFunction<? super V, R, E> transform) throws E { 082 var result = newInstanceOf((Map<K, R>) source).orElse(new DefaultMap<>()); 083 084 for (var entry : source.entrySet()) { 085 result.put(entry.getKey(), transform.apply(entry.getValue())); 086 } 087 088 return result instanceof DefaultMap ? Map.copyOf(result) : result; 089 } 090 091 private static final class DefaultMap<K, V> extends HashMap<K, V> {} 092 093}