001/* 002 * Portions of this software was developed by employees of the National Institute 003 * of Standards and Technology (NIST), an agency of the Federal Government and is 004 * being made available as a public service. Pursuant to title 17 United States 005 * Code Section 105, works of NIST employees are not subject to copyright 006 * protection in the United States. This software may be subject to foreign 007 * copyright. Permission in the United States and in foreign countries, to the 008 * extent that NIST may hold copyright, to use, copy, modify, create derivative 009 * works, and distribute this software and its documentation without fee is hereby 010 * granted on a non-exclusive basis, provided that this notice and disclaimer 011 * of warranty appears in all copies. 012 * 013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM 017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE 018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT 019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, 020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, 021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, 022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR 023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT 024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. 025 */ 026 027package gov.nist.secauto.oscal.lib.profile.resolver; 028 029import gov.nist.secauto.metaschema.model.common.util.CollectionUtil; 030 031import java.util.Collections; 032import java.util.LinkedHashMap; 033import java.util.List; 034import java.util.Map; 035import java.util.Objects; 036import java.util.function.Function; 037import java.util.stream.Collectors; 038import java.util.stream.Stream; 039 040import edu.umd.cs.findbugs.annotations.NonNull; 041import edu.umd.cs.findbugs.annotations.Nullable; 042 043public final class ModifyPhaseUtils { 044 private ModifyPhaseUtils() { 045 // disable construction 046 } 047 048 public static <T> Function<? super T, String> identityKey() { 049 return (item) -> Integer.toString(Objects.hashCode(item)); 050 } 051 052 public static <T, R> Function<? super T, String> identifierKey(@NonNull Function<T, R> identifierFunction) { 053 return (item) -> { 054 R identifier = identifierFunction.apply(item); 055 String retval; 056 if (identifier == null) { 057 retval = Integer.toString(Objects.hashCode(item)); 058 } else { 059 retval = identifier.toString(); 060 } 061 return retval; 062 }; 063 } 064 065 @SuppressWarnings("PMD.OnlyOneReturn") // readability 066 public static <T> T mergeItem(@Nullable T original, @Nullable T additional) { 067 if (additional == null) { 068 return original; 069 } 070 071 return additional; 072 } 073 074 @SuppressWarnings("PMD.OnlyOneReturn") // readability 075 public static <T> List<T> merge(@Nullable List<T> original, @Nullable List<T> additional, 076 Function<? super T, String> keyFunction) { 077 if (additional == null || additional.isEmpty()) { 078 return original; 079 } 080 081 if (original == null || original.isEmpty()) { 082 return additional; 083 } 084 085 // reverse the stream 086 List<T> reversed = Stream.concat( 087 CollectionUtil.listOrEmpty(original).stream(), 088 CollectionUtil.listOrEmpty(additional).stream()) 089 .collect(Collectors.collectingAndThen( 090 Collectors.toList(), 091 l -> { 092 Collections.reverse(l); 093 return l; 094 })); 095 096 // build a map of each unique identity 097 Map<String, List<T>> identityMap = reversed.stream() 098 .collect(Collectors.groupingBy(keyFunction, LinkedHashMap::new, Collectors.toList())); 099 100 // build a reversed list of items, using the first item 101 return identityMap.values().stream() 102 .map(list -> list.stream().findFirst().get()) 103 .collect(Collectors.collectingAndThen( 104 Collectors.toList(), 105 l -> { 106 Collections.reverse(l); 107 return l; 108 })); 109 } 110}