001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package gov.nist.secauto.oscal.lib.profile.resolver;
007
008import gov.nist.secauto.metaschema.core.util.CollectionUtil;
009
010import java.util.Collections;
011import java.util.LinkedHashMap;
012import java.util.List;
013import java.util.Map;
014import java.util.Objects;
015import java.util.function.Function;
016import java.util.stream.Collectors;
017import java.util.stream.Stream;
018
019import edu.umd.cs.findbugs.annotations.NonNull;
020import edu.umd.cs.findbugs.annotations.Nullable;
021
022public final class ModifyPhaseUtils {
023  private ModifyPhaseUtils() {
024    // disable construction
025  }
026
027  public static <T> Function<? super T, String> identityKey() {
028    return item -> Integer.toString(Objects.hashCode(item));
029  }
030
031  public static <T, R> Function<? super T, String> identifierKey(@NonNull Function<T, R> identifierFunction) {
032    return item -> {
033      R identifier = identifierFunction.apply(item);
034      String retval;
035      if (identifier == null) {
036        retval = Integer.toString(Objects.hashCode(item));
037      } else {
038        retval = identifier.toString();
039      }
040      return retval;
041    };
042  }
043
044  @SuppressWarnings("PMD.OnlyOneReturn") // readability
045  public static <T> T mergeItem(@Nullable T original, @Nullable T additional) {
046    if (additional == null) {
047      return original;
048    }
049
050    return additional;
051  }
052
053  @SuppressWarnings("PMD.OnlyOneReturn") // readability
054  public static <T> List<T> merge(@Nullable List<T> original, @Nullable List<T> additional,
055      Function<? super T, String> keyFunction) {
056    if (additional == null || additional.isEmpty()) {
057      return original;
058    }
059
060    if (original == null || original.isEmpty()) {
061      return additional;
062    }
063
064    // reverse the stream
065    List<T> reversed = Stream.concat(
066        CollectionUtil.listOrEmpty(original).stream(),
067        CollectionUtil.listOrEmpty(additional).stream())
068        .collect(Collectors.collectingAndThen(
069            Collectors.toList(),
070            l -> {
071              Collections.reverse(l);
072              return l;
073            }));
074
075    // build a map of each unique identity
076    Map<String, List<T>> identityMap = reversed.stream()
077        .collect(Collectors.groupingBy(keyFunction, LinkedHashMap::new, Collectors.toList()));
078
079    // build a reversed list of items, using the first item
080    return identityMap.values().stream()
081        .map(list -> list.stream().findFirst().get())
082        .collect(Collectors.collectingAndThen(
083            Collectors.toList(),
084            l -> {
085              Collections.reverse(l);
086              return l;
087            }));
088  }
089}