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.mutableList; 019import static java.util.Objects.requireNonNull; 020 021import java.lang.reflect.Array; 022import java.util.ArrayList; 023import java.util.Collection; 024import java.util.Collections; 025import java.util.Comparator; 026import java.util.HashSet; 027import java.util.Iterator; 028import java.util.List; 029import java.util.NavigableSet; 030import java.util.Optional; 031import java.util.Set; 032import java.util.TreeSet; 033import java.util.concurrent.ConcurrentSkipListSet; 034import java.util.concurrent.CopyOnWriteArrayList; 035import java.util.concurrent.CopyOnWriteArraySet; 036import java.util.stream.Stream; 037 038import lombok.EqualsAndHashCode; 039import lombok.Getter; 040import lombok.ToString; 041 042/** 043 * <h2>Overview</h2> Builder for creating {@link java.util.Collection}s 044 * providing some convenience methods. The class writes everything through into 045 * the contained collector. Using the default constructor a newly created 046 * {@link java.util.ArrayList} will be used as collector, but you can pass you 047 * own collector as constructor-argument. Of course this should be mutable in 048 * order to work. 049 * <h3>Handling of null-values</h3> 050 * <p> 051 * As default {@code null} values are ignored. This behavior can be changed by 052 * call {@link #addNullValues(boolean)}. <em>Caution:</em> In case of using one 053 * of the {@link #copyFrom(Collection)} methods for instantiation the 054 * {@code null} values will not be checked in that way 055 * </p> 056 * <h3>Standard Usage</h3> 057 * 058 * <pre> 059 * 060 * List<String> result = new CollectionBuilder<String>().add("this").add("that").add(mutableList("on", "or an other")) 061 * .toImmutableList(); 062 * </pre> 063 * 064 * or 065 * 066 * <pre> 067 * 068 * Set<String> result = new CollectionBuilder<String>().add("this").add("that").add(mutableList("on", "or an other")) 069 * .toMutableSet(); 070 * </pre> 071 * 072 * <h3>Copy From</h3> This methods can be used for ensuring a real copy 073 * 074 * <pre> 075 * 076 * List<String> result = CollectionBuilder.copyFrom(mutableList("on", "or an other")).add("element").toMutableList(); 077 * 078 * </pre> 079 * 080 * <h3>Sorting</h3> 081 * <p> 082 * The contained {@link java.util.Collection} can be sorted any time by calling 083 * {@link #sort(Comparator)} 084 * </p> 085 * 086 * @author Oliver Wolff 087 * @param <E> The type of the contained {@link java.util.Collection} 088 */ 089@EqualsAndHashCode 090@ToString 091public final class CollectionBuilder<E> implements Iterable<E> { 092 093 private final Collection<E> collector; 094 095 /** 096 * If set to {@code true} {@code null} elements are added to the contained 097 * collector, if {@code false}, default value, {@code null} values are ignored. 098 */ 099 @Getter 100 private boolean addNullValues = false; 101 102 /** 103 * <p> 104 * Constructor for CollectionBuilder. 105 * </p> 106 * 107 * @param collector to be used for storage. Must not be null 108 */ 109 public CollectionBuilder(Collection<E> collector) { 110 this.collector = requireNonNull(collector); 111 } 112 113 /** 114 * Default Constructor initializing the collector with an 115 * {@link java.util.ArrayList} 116 */ 117 public CollectionBuilder() { 118 this(new ArrayList<>()); 119 } 120 121 /** 122 * <p> 123 * addNullValues. 124 * </p> 125 * 126 * @param addNullValues If set to {@code true} {@code null} elements are added 127 * to the contained collector, if {@code false}, default 128 * value, {@code null} values are ignored. 129 * @return the instance itself in order to use it in a fluent way. 130 */ 131 public CollectionBuilder<E> addNullValues(boolean addNullValues) { 132 this.addNullValues = addNullValues; 133 return this; 134 } 135 136 /** 137 * <p> 138 * size. 139 * </p> 140 * 141 * @return the size of the contained Collection 142 */ 143 public int size() { 144 return collector.size(); 145 } 146 147 /** 148 * <p> 149 * isEmpty. 150 * </p> 151 * 152 * @return see {@link java.util.Collection#isEmpty()} 153 */ 154 public boolean isEmpty() { 155 return collector.isEmpty(); 156 } 157 158 /** 159 * <p> 160 * contains. 161 * </p> 162 * 163 * @param o a {@link java.lang.Object} object 164 * @return see {@link java.util.Collection#isEmpty()} 165 */ 166 public boolean contains(Object o) { 167 return collector.contains(o); 168 } 169 170 /** {@inheritDoc} */ 171 @Override 172 public Iterator<E> iterator() { 173 return collector.iterator(); 174 } 175 176 /** 177 * <p> 178 * stream. 179 * </p> 180 * 181 * @return a {@link java.util.stream.Stream} on the contained objects 182 */ 183 public Stream<E> stream() { 184 return collector.stream(); 185 } 186 187 /** 188 * <p> 189 * add. 190 * </p> 191 * 192 * @param e the element to be added 193 * @return the instance itself in order to use it in a fluent way. 194 * <em>Caution:</em> with this call the return value of 195 * {@link java.util.Collection#add(Object)} will be ignored. 196 */ 197 public CollectionBuilder<E> add(E e) { 198 if (addNullValues || null != e) { 199 collector.add(e); 200 } 201 return this; 202 } 203 204 /** 205 * <p> 206 * add. 207 * </p> 208 * 209 * @param elements to be added 210 * @return the instance itself in order to use it in a fluent way. 211 * <em>Caution:</em> with this call the return value of 212 * {@link java.util.Collection#add(Object)} will be ignored. 213 */ 214 @SafeVarargs 215 public final CollectionBuilder<E> add(E... elements) { 216 if (!MoreCollections.isEmpty(elements)) { 217 for (E element : elements) { 218 add(element); 219 } 220 } 221 return this; 222 } 223 224 /** 225 * <p> 226 * add. 227 * </p> 228 * 229 * @param elements to be added 230 * @return the instance itself in order to use it in a fluent way. 231 * <em>Caution:</em> with this call the return value of 232 * {@link java.util.Collection#add(Object)} will be ignored. 233 */ 234 public CollectionBuilder<E> add(Iterable<E> elements) { 235 if (!MoreCollections.isEmpty(elements)) { 236 elements.forEach(this::add); 237 } 238 return this; 239 } 240 241 /** 242 * <p> 243 * add. 244 * </p> 245 * 246 * @param elements to be added 247 * @return the instance itself in order to use it in a fluent way. 248 * <em>Caution:</em> with this call the return value of 249 * {@link java.util.Collection#add(Object)} will be ignored. 250 */ 251 public CollectionBuilder<E> add(Collection<E> elements) { 252 if (!MoreCollections.isEmpty(elements)) { 253 elements.forEach(this::add); 254 } 255 return this; 256 } 257 258 /** 259 * <p> 260 * add. 261 * </p> 262 * 263 * @param element to be added if present. <em>Caution</em>: passing an 264 * {@link java.util.Optional} parameter is a not a good thing to 265 * do, I know, but in this context it is quite convenient: Don't 266 * do this at home 267 * @return the instance itself in order to use it in a fluent way. 268 * <em>Caution:</em> with this call the return value of 269 * {@link java.util.Collection#add(Object)} will be ignored. 270 */ 271 public CollectionBuilder<E> add(Optional<E> element) { 272 element.ifPresent(this::add); 273 return this; 274 } 275 276 /** 277 * <p> 278 * add. 279 * </p> 280 * 281 * @param elements to be added 282 * @return the instance itself in order to use it in a fluent way. 283 * <em>Caution:</em> with this call the return value of 284 * {@link java.util.Collection#add(Object)} will be ignored. 285 */ 286 public CollectionBuilder<E> add(Stream<E> elements) { 287 if (!MoreCollections.isEmpty(elements)) { 288 elements.forEach(this::add); 289 } 290 return this; 291 } 292 293 /** 294 * Sorts the contained Collection. 295 * 296 * @param comparator must not be null. 297 * @return the instance itself in order to use it in a fluent way. 298 */ 299 public CollectionBuilder<E> sort(Comparator<? super E> comparator) { 300 if (collector instanceof List) { 301 Collections.sort((List<E>) collector, comparator); 302 } else { 303 List<E> sorter = new ArrayList<>(collector); 304 Collections.sort(sorter, comparator); 305 collector.clear(); 306 collector.addAll(sorter); 307 } 308 return this; 309 } 310 311 /** 312 * <p> 313 * toMutableList. 314 * </p> 315 * 316 * @return a mutable {@link java.util.List} representation of the builders 317 * content, the actual implementation is an {@link java.util.ArrayList} 318 */ 319 public List<E> toMutableList() { 320 return new ArrayList<>(collector); 321 } 322 323 /** 324 * <p> 325 * toImmutableList. 326 * </p> 327 * 328 * @return an immutable {@link java.util.List} representation of the builders 329 * content, the actual implementation calls 330 * {@link java.util.Collections#unmodifiableList(List)}. The underlying 331 * {@link java.util.Collection} will be copied by calling 332 * {@link #toMutableList()} 333 */ 334 public List<E> toImmutableList() { 335 return Collections.unmodifiableList(toMutableList()); 336 } 337 338 /** 339 * <p> 340 * toMutableSet. 341 * </p> 342 * 343 * @return a mutable {@link java.util.Set} representation of the builders 344 * content, the actual implementation is an {@link java.util.HashSet} 345 */ 346 public Set<E> toMutableSet() { 347 return new HashSet<>(collector); 348 } 349 350 /** 351 * <p> 352 * toImmutableSet. 353 * </p> 354 * 355 * @return an immutable {@link java.util.Set} representation of the builders 356 * content, the actual implementation calls 357 * {@link java.util.Collections#unmodifiableList(List)}. The underlying 358 * {@link java.util.Collection} will be copied by calling 359 * {@link #toMutableSet()} 360 */ 361 public Set<E> toImmutableSet() { 362 return Collections.unmodifiableSet(toMutableSet()); 363 } 364 365 /** 366 * <p> 367 * toConcurrentList. 368 * </p> 369 * 370 * @return a concurrent mutable {@link java.util.List} representation of the 371 * builders content, the actual implementation is an 372 * {@link java.util.concurrent.CopyOnWriteArrayList} 373 */ 374 public List<E> toConcurrentList() { 375 return new CopyOnWriteArrayList<>(collector); 376 } 377 378 /** 379 * <p> 380 * toConcurrentSet. 381 * </p> 382 * 383 * @return a concurrent mutable {@link java.util.Set} representation of the 384 * builders content, the actual implementation is an 385 * {@link java.util.concurrent.CopyOnWriteArraySet} 386 */ 387 public Set<E> toConcurrentSet() { 388 return new CopyOnWriteArraySet<>(collector); 389 } 390 391 /** 392 * <p> 393 * toMutableNavigableSet. 394 * </p> 395 * 396 * @return a mutable {@link java.util.NavigableSet} representation of the 397 * builders content, the actual implementation is an 398 * {@link java.util.TreeSet}. The assumption is that the Actual type is 399 * at least {@link java.lang.Comparable} 400 */ 401 public NavigableSet<E> toMutableNavigableSet() { 402 return new TreeSet<>(collector); 403 } 404 405 /** 406 * <p> 407 * toImmutableNavigableSet. 408 * </p> 409 * 410 * @return an immutable {@link java.util.NavigableSet} representation of the 411 * builders content, the actual implementation is an 412 * {@link java.util.TreeSet} wrapped by 413 * {@link java.util.Collections#unmodifiableNavigableSet(NavigableSet)}. 414 * The assumption is that the Actual type is at least 415 * {@link java.lang.Comparable} 416 */ 417 public NavigableSet<E> toImmutableNavigableSet() { 418 return Collections.unmodifiableNavigableSet(toMutableNavigableSet()); 419 } 420 421 /** 422 * <p> 423 * toConcurrentNavigableSet. 424 * </p> 425 * 426 * @return a mutable {@link java.util.NavigableSet} representation of the 427 * builders content, the actual implementation is an 428 * {@link java.util.concurrent.ConcurrentSkipListSet}. The assumption is 429 * that the actual type is at least {@link java.lang.Comparable} 430 */ 431 public NavigableSet<E> toConcurrentNavigableSet() { 432 return new ConcurrentSkipListSet<>(collector); 433 } 434 435 /** 436 * <p> 437 * toArray. 438 * </p> 439 * 440 * @param targetType identifying the concrete ArrayType 441 * @return an array representation of the builders content 442 */ 443 @SuppressWarnings("unchecked") 444 public E[] toArray(Class<? super E> targetType) { 445 if (isEmpty()) { 446 return (E[]) Array.newInstance(targetType, 0); 447 } 448 var target = (E[]) Array.newInstance(targetType, size()); 449 return collector.toArray(target); 450 } 451 452 /** 453 * Clears the elements in the collector 454 * 455 * @return the instance itself in order to use it in a fluent way. 456 */ 457 public CollectionBuilder<E> clear() { 458 collector.clear(); 459 return this; 460 } 461 462 /** 463 * Creates an Instance of {@link de.cuioss.tools.collect.CollectionBuilder} by 464 * copying the content of the given source <em>Caution:</em> The given source 465 * will be used as it is, there will be no filtering as defined within 466 * {@link #addNullValues(boolean)} 467 * 468 * @param source may be null 469 * @return the newly created {@link de.cuioss.tools.collect.CollectionBuilder} 470 * @param <E> a E class 471 */ 472 public static <E> CollectionBuilder<E> copyFrom(Iterable<? extends E> source) { 473 return new CollectionBuilder<>(mutableList(source)); 474 } 475 476 /** 477 * Creates an Instance of {@link de.cuioss.tools.collect.CollectionBuilder} by 478 * copying the content of the given source <em>Caution:</em> The given source 479 * will be used as it is, there will be no filtering as defined within 480 * {@link #addNullValues(boolean)} 481 * 482 * @param source may be null 483 * @return the newly created {@link de.cuioss.tools.collect.CollectionBuilder} 484 * @param <E> a E class 485 */ 486 public static <E> CollectionBuilder<E> copyFrom(Iterator<? extends E> source) { 487 return new CollectionBuilder<>(mutableList(source)); 488 } 489 490 /** 491 * Creates an Instance of {@link de.cuioss.tools.collect.CollectionBuilder} by 492 * copying the content of the given source <em>Caution:</em> The given source 493 * will be used as it is, there will be no filtering as defined within 494 * {@link #addNullValues(boolean)} 495 * 496 * @param source may be null 497 * @return the newly created {@link de.cuioss.tools.collect.CollectionBuilder} 498 * @param <E> a E class 499 */ 500 public static <E> CollectionBuilder<E> copyFrom(Collection<? extends E> source) { 501 return new CollectionBuilder<>(mutableList(source)); 502 } 503 504 /** 505 * Creates an Instance of {@link de.cuioss.tools.collect.CollectionBuilder} by 506 * copying the content of the given source <em>Caution:</em> The given source 507 * will be used as it is, there will be no filtering as defined within 508 * {@link #addNullValues(boolean)} 509 * 510 * @param source may be null 511 * @return the newly created {@link de.cuioss.tools.collect.CollectionBuilder} 512 * @param <E> a E class 513 */ 514 public static <E> CollectionBuilder<E> copyFrom(Stream<? extends E> source) { 515 return new CollectionBuilder<>(mutableList(source)); 516 } 517 518 /** 519 * Creates an Instance of {@link de.cuioss.tools.collect.CollectionBuilder} by 520 * copying the content of the given source <em>Caution:</em> The given source 521 * will be used as it is, there will be no filtering as defined within 522 * {@link #addNullValues(boolean)} 523 * 524 * @param source may be null 525 * @return the newly created {@link de.cuioss.tools.collect.CollectionBuilder} 526 * @param <E> a E class 527 */ 528 @SafeVarargs 529 public static <E> CollectionBuilder<E> copyFrom(E... source) { 530 return new CollectionBuilder<>(mutableList(source)); 531 } 532 533 /** 534 * Creates an Instance of {@link de.cuioss.tools.collect.CollectionBuilder} by 535 * copying the content of the given source <em>Caution:</em> The given source 536 * will be used as it is, there will be no filtering as defined within 537 * {@link #addNullValues(boolean)} 538 * 539 * @param source may be null 540 * @return the newly created {@link de.cuioss.tools.collect.CollectionBuilder} 541 * @param <E> a E class 542 */ 543 public static <E> CollectionBuilder<E> copyFrom(E source) { 544 return new CollectionBuilder<>(mutableList(source)); 545 } 546 547}