001/* 002 * Copyright 2020 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.List; 020import java.util.Map; 021import java.util.function.BinaryOperator; 022import java.util.stream.Collectors; 023import java.util.stream.Stream; 024 025/** 026 * Utility methods for {@link Map}s 027 */ 028public final class MapUtils 029{ 030 private static BinaryOperator<?> __DEFAULT_MERGE_FUNCTION = (value1, value2) -> value1; 031 032 private MapUtils() 033 { 034 // Nothing 035 } 036 037 @SuppressWarnings("unchecked") 038 private static <V> BinaryOperator<V> _defaultMergeFunction() 039 { 040 return (BinaryOperator<V>) __DEFAULT_MERGE_FUNCTION; 041 } 042 043 /** 044 * Merges the entries of two maps into one map. Entries of the first one have the priority (i.e. kept if same keys) over ones of the second one. 045 * @param <K> The type of the keys 046 * @param <V> The type of the values 047 * @param map1 The first map (not modified) 048 * @param map2 The second map (not modified) 049 * @return The merged map 050 */ 051 public static <K, V> Map<K, V> merge( 052 Map<? extends K, ? extends V> map1, 053 Map<? extends K, ? extends V> map2) 054 { 055 Stream<Map<? extends K, ? extends V>> sequentialStreamOfMaps = Stream.of(map1, map2); 056 return _merge(_defaultMergeFunction(), sequentialStreamOfMaps); 057 } 058 059 060 /** 061 * Merges the entries of several maps into one map. Entries of any input map one have the priority (i.e. kept if same keys) over ones of any following map. 062 * @param <K> The type of the keys 063 * @param <V> The type of the values 064 * @param map1 The first map (not modified) 065 * @param map2 The second map (not modified) 066 * @param others The other maps (not modified) 067 * @return The merged map 068 */ 069 @SafeVarargs // Creating a stream from an array is safe 070 public static <K, V> Map<K, V> merge( 071 Map<? extends K, ? extends V> map1, 072 Map<? extends K, ? extends V> map2, 073 Map<? extends K, ? extends V>... others) 074 { 075 return merge(_defaultMergeFunction(), map1, map2, others); 076 } 077 078 /** 079 * Merges the entries of two maps into one map. 080 * @param <K> The type of the keys 081 * @param <V> The type of the values 082 * @param mergeFunction The merging function, to determine which value of the entry to keep when same keys are encountered. 083 * @param map1 The first map (not modified) 084 * @param map2 The second map (not modified) 085 * @return The merged map 086 */ 087 public static <K, V> Map<K, V> merge( 088 BinaryOperator<V> mergeFunction, 089 Map<? extends K, ? extends V> map1, 090 Map<? extends K, ? extends V> map2) 091 { 092 Stream<Map<? extends K, ? extends V>> sequentialStreamOfMaps = Stream.of(map1, map2); 093 return _merge(mergeFunction, sequentialStreamOfMaps); 094 } 095 096 /** 097 * Merges the entries of several maps into one map. Entries of any input map one have the priority (i.e. kept if same keys) over ones of any following map. 098 * @param <K> The type of the keys 099 * @param <V> The type of the values 100 * @param mergeFunction The merging function, to determine which value of the entry to keep when same keys are encountered. 101 * @param map1 The first map (not modified) 102 * @param map2 The second map (not modified) 103 * @param others The other maps (not modified) 104 * @return The merged map 105 */ 106 @SafeVarargs // Creating a stream from an array is safe 107 public static <K, V> Map<K, V> merge( 108 BinaryOperator<V> mergeFunction, 109 Map<? extends K, ? extends V> map1, 110 Map<? extends K, ? extends V> map2, 111 Map<? extends K, ? extends V>... others) 112 { 113 Stream<Map<? extends K, ? extends V>> sequentialStreamOfMaps = Stream.of( 114 Stream.of(map1, map2), 115 Stream.of(others) 116 ) 117 .flatMap(stream -> stream) 118 .sequential(); 119 120 return _merge(mergeFunction, sequentialStreamOfMaps); 121 } 122 123 124 private static <K, V> Map<K, V> _merge( 125 BinaryOperator<V> mergeFunction, 126 Stream<Map<? extends K, ? extends V>> sequentialStreamOfMaps) 127 { 128 return sequentialStreamOfMaps 129 .map(Map::entrySet) 130 .map(ArrayList::new) // Convert Set into List in order to be sure entries of a given map all come before any entry of a following map 131 .flatMap(List::stream) 132 .collect(Collectors.toMap( 133 Map.Entry::getKey, 134 Map.Entry::getValue, 135 mergeFunction)); 136 } 137}