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}