001/* 002 * Copyright 2023 the original author or authors. 003 * <p> 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 * <p> 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * <p> 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 de.cuioss.tools.collect; 017 018import static de.cuioss.tools.collect.CollectionLiterals.mutableMap; 019import static java.util.Objects.requireNonNull; 020 021import java.util.Collections; 022import java.util.HashMap; 023import java.util.Map; 024import java.util.Map.Entry; 025 026import lombok.EqualsAndHashCode; 027import lombok.ToString; 028 029/** 030 * <h2>Overview</h2> 031 * <p> 032 * Builder for creating {@link java.util.Map}s providing some convenience 033 * methods. The class writes everything through into the contained collector. 034 * Using the default constructor a newly created {@link java.util.HashMap} will 035 * be used as collector, but you can pass you own collector as 036 * constructor-argument. Of course this should be mutable in order to work. 037 * </p> 038 * <p> 039 * Although not being a {@link java.util.Map} itself it provides the same 040 * methods with different semantics -> Builder approach. 041 * </p> 042 * <h3>Standard Usage</h3> 043 * 044 * <pre> 045 * MapBuilder<String, String> builder = new MapBuilder<>(); 046 * builder.put("key1", "value1").put("key2", "value2"); 047 * assertEquals(2, builder.size()); 048 * assertMutable(builder.toMutableMap()); 049 * assertImmutable(builder.toImmutableMap()); 050 * </pre> 051 * 052 * <h3>Using from()</h3> This methods can be used for ensuring a real copy 053 * 054 * <pre> 055 * assertEquals(4, MapBuilder.from("key1", 1, "key2", 2, "key3", 3, "key4", 4).size()); 056 * </pre> 057 * 058 * @author Oliver Wolff 059 * @param <K> the type of keys maintained by this map 060 * @param <V> the type of mapped values 061 */ 062@EqualsAndHashCode 063@ToString 064public final class MapBuilder<K, V> { 065 066 private final Map<K, V> collector; 067 068 /** 069 * Default Constructor initializing the collector with an 070 * {@link java.util.HashMap} 071 */ 072 public MapBuilder() { 073 this(new HashMap<>()); 074 } 075 076 /** 077 * <p> 078 * Constructor for MapBuilder. 079 * </p> 080 * 081 * @param collector to be used for storage. Must not be null 082 */ 083 public MapBuilder(Map<K, V> collector) { 084 this.collector = requireNonNull(collector); 085 } 086 087 /** 088 * <p> 089 * size. 090 * </p> 091 * 092 * @return the number of key-value mappings in this map 093 * @see Map#size() 094 */ 095 public int size() { 096 return collector.size(); 097 } 098 099 /** 100 * <p> 101 * isEmpty. 102 * </p> 103 * 104 * @return true if this map contains no key-value mappings 105 * @see Map#isEmpty() 106 */ 107 public boolean isEmpty() { 108 return collector.isEmpty(); 109 } 110 111 /** 112 * Returns {@code true} if this map contains a mapping for the specified key. 113 * More formally, returns {@code true} if and only if this map contains a 114 * mapping for a key {@code k} such that 115 * {@code (key==null ? k==null : key.equals(k))}. (There can be at most one such 116 * mapping.) 117 * 118 * @param key key whose presence in this map is to be tested 119 * @return {@code true} if this map contains a mapping for the specified key 120 * @see Map#containsKey(Object) 121 */ 122 public boolean containsKey(Object key) { 123 return collector.containsKey(key); 124 } 125 126 /** 127 * Returns {@code true} if this map maps one or more keys to the specified 128 * value. More formally, returns {@code true} if and only if this map contains 129 * at least one mapping to a value {@code v} such that 130 * {@code (value==null ? v==null : value.equals(v))}. This operation will 131 * probably require time linear in the map size for most implementations of the 132 * {@code Map} interface. 133 * 134 * @param value value whose presence in this map is to be tested 135 * @return {@code true} if this map maps one or more keys to the specified value 136 * @see Map#containsValue(Object) 137 */ 138 public boolean containsValue(Object value) { 139 return collector.containsValue(value); 140 } 141 142 /** 143 * <p> 144 * put. 145 * </p> 146 * 147 * @param key to be put as key, must not be empty 148 * @param value to be put as value, must not be empty 149 * @return the instance itself in order to use it in a fluent way. 150 */ 151 public MapBuilder<K, V> put(K key, V value) { 152 collector.put(key, value); 153 return this; 154 } 155 156 /** 157 * Puts the entry into the map, if the value is not {@code null}. 158 * 159 * @param key to be put as key, must not be empty 160 * @param value to be put as value 161 * @return the instance itself in order to use it in a fluent way. 162 */ 163 public MapBuilder<K, V> putIfNotNull(K key, V value) { 164 if (null != value) { 165 collector.put(key, value); 166 } 167 return this; 168 } 169 170 /** 171 * <p> 172 * put. 173 * </p> 174 * 175 * @param entry to be put, must not {@code null} 176 * @return the instance itself in order to use it in a fluent way. 177 */ 178 public MapBuilder<K, V> put(Entry<? extends K, ? extends V> entry) { 179 collector.put(entry.getKey(), entry.getValue()); 180 return this; 181 } 182 183 /** 184 * <p> 185 * remove. 186 * </p> 187 * 188 * @param key to be removed 189 * @return the instance itself in order to use it in a fluent way. 190 */ 191 public MapBuilder<K, V> remove(Object key) { 192 collector.remove(key); 193 return this; 194 } 195 196 /** 197 * <p> 198 * putAll. 199 * </p> 200 * 201 * @param map to be put 202 * @return the instance itself in order to use it in a fluent way. 203 */ 204 public MapBuilder<K, V> putAll(Map<? extends K, ? extends V> map) { 205 collector.putAll(map); 206 return this; 207 } 208 209 /** 210 * Clears the contained collector 211 * 212 * @return the instance itself in order to use it in a fluent way. 213 */ 214 public MapBuilder<K, V> clear() { 215 collector.clear(); 216 return this; 217 } 218 219 /** 220 * <p> 221 * toMutableMap. 222 * </p> 223 * 224 * @return a mutable {@link java.util.Map} representation of the builders 225 * content, the actual implementation is a {@link java.util.HashMap} 226 */ 227 public Map<K, V> toMutableMap() { 228 return new HashMap<>(collector); 229 } 230 231 /** 232 * <p> 233 * toImmutableMap. 234 * </p> 235 * 236 * @return an immutable {@link java.util.Map} representation of the builders 237 * content, the actual implementation does not create a copy but 238 * provides an unmodifiable view using 239 * {@link java.util.Collections#unmodifiableMap(Map)} 240 */ 241 public Map<K, V> toImmutableMap() { 242 return Collections.unmodifiableMap(collector); 243 } 244 245 /** 246 * <p> 247 * copyFrom. 248 * </p> 249 * 250 * @param <K> the type of keys maintained by this map 251 * @param <V> the type of mapped values 252 * @param original map used to initialize the contained collector 253 * @return an instance of {@link de.cuioss.tools.collect.MapBuilder} initialized 254 * with a copy of the given Map. 255 */ 256 public static <K, V> MapBuilder<K, V> copyFrom(Map<K, V> original) { 257 return new MapBuilder<>(new HashMap<>(original)); 258 } 259 260 /** 261 * Shorthand for creating a {@link de.cuioss.tools.collect.MapBuilder} from a 262 * given key/value pair 263 * 264 * @param <K> the type of keys maintained by this map 265 * @param <V> the type of mapped values 266 * @param k1 key to be added 267 * @param v1 value to be added 268 * @return an instance of {@link de.cuioss.tools.collect.MapBuilder} initialized 269 * with the given key/value pair. 270 */ 271 public static <K, V> MapBuilder<K, V> from(K k1, V v1) { 272 return new MapBuilder<>(mutableMap(k1, v1)); 273 } 274 275 /** 276 * Shorthand for creating a {@link de.cuioss.tools.collect.MapBuilder} from 277 * given key/value pairs 278 * 279 * @param <K> the type of keys maintained by this map 280 * @param <V> the type of mapped values 281 * @param k1 key to be added 282 * @param v1 value to be added 283 * @param k2 key to be added 284 * @param v2 value to be added 285 * @return an instance of {@link de.cuioss.tools.collect.MapBuilder} initialized 286 * with the given key/value pairs. 287 */ 288 public static <K, V> MapBuilder<K, V> from(K k1, V v1, K k2, V v2) { 289 return new MapBuilder<>(mutableMap(k1, v1, k2, v2)); 290 } 291 292 /** 293 * Shorthand for creating a {@link de.cuioss.tools.collect.MapBuilder} from 294 * given key/value pairs 295 * 296 * @param <K> the type of keys maintained by this map 297 * @param <V> the type of mapped values 298 * @param k1 key to be added 299 * @param v1 value to be added 300 * @param k2 key to be added 301 * @param v2 value to be added 302 * @param k3 key to be added 303 * @param v3 value to be added 304 * @return an instance of {@link de.cuioss.tools.collect.MapBuilder} initialized 305 * with the given key/value pairs. 306 */ 307 public static <K, V> MapBuilder<K, V> from(K k1, V v1, K k2, V v2, K k3, V v3) { 308 return new MapBuilder<>(mutableMap(k1, v1, k2, v2, k3, v3)); 309 } 310 311 /** 312 * Shorthand for creating a {@link de.cuioss.tools.collect.MapBuilder} from 313 * given key/value pairs 314 * 315 * @param <K> the type of keys maintained by this map 316 * @param <V> the type of mapped values 317 * @param k1 key to be added 318 * @param v1 value to be added 319 * @param k2 key to be added 320 * @param v2 value to be added 321 * @param k3 key to be added 322 * @param v3 value to be added 323 * @param k4 key to be added 324 * @param v4 value to be added 325 * @return an instance of {@link de.cuioss.tools.collect.MapBuilder} initialized 326 * with the given key/value pairs. 327 */ 328 @SuppressWarnings("squid:S00107") // owolff: Number of parameters match to the use-case 329 public static <K, V> MapBuilder<K, V> from(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { 330 return new MapBuilder<>(mutableMap(k1, v1, k2, v2, k3, v3, k4, v4)); 331 } 332}