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