001/*- 002 * #%L 003 * HAPI FHIR Storage api 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.jpa.dao; 021 022import com.google.common.collect.Multimap; 023import com.google.common.collect.MultimapBuilder; 024import org.apache.commons.lang3.tuple.Pair; 025import org.hl7.fhir.instance.model.api.IIdType; 026 027import java.util.HashMap; 028import java.util.List; 029import java.util.Map; 030import java.util.Objects; 031import java.util.stream.Collectors; 032 033public class IdSubstitutionMap { 034 035 private final Map<Entry, Entry> myMap = new HashMap<>(); 036 private final Multimap<Entry, Entry> myReverseMap = 037 MultimapBuilder.hashKeys().arrayListValues().build(); 038 039 public boolean containsSource(IIdType theId) { 040 if (theId.isLocal()) { 041 return false; 042 } 043 return myMap.containsKey(new Entry(theId)); 044 } 045 046 public boolean containsSource(String theId) { 047 return myMap.containsKey(new Entry(theId)); 048 } 049 050 public boolean containsTarget(IIdType theId) { 051 return myReverseMap.containsKey(new Entry(theId)); 052 } 053 054 public boolean containsTarget(String theId) { 055 return myReverseMap.containsKey(new Entry(theId)); 056 } 057 058 public IIdType getForSource(IIdType theId) { 059 Entry target = myMap.get(new Entry(theId)); 060 if (target != null) { 061 assert target.myId != null; 062 return target.myId; 063 } 064 return null; 065 } 066 067 public IIdType getForSource(String theId) { 068 Entry target = myMap.get(new Entry(theId)); 069 if (target != null) { 070 assert target.myId != null; 071 return target.myId; 072 } 073 return null; 074 } 075 076 public List<Pair<IIdType, IIdType>> entrySet() { 077 return myMap.entrySet().stream() 078 .map(t -> Pair.of(t.getKey().myId, t.getValue().myId)) 079 .collect(Collectors.toList()); 080 } 081 082 public void put(IIdType theSource, IIdType theTarget) { 083 myMap.put(new Entry(theSource), new Entry(theTarget)); 084 myReverseMap.put(new Entry(theTarget), new Entry(theSource)); 085 } 086 087 public boolean isEmpty() { 088 return myMap.isEmpty(); 089 } 090 091 /** 092 * Updates all targets of the map with a new id value if the input id has 093 * the same ResourceType and IdPart as the target id. 094 */ 095 public void updateTargets(IIdType theNewId) { 096 if (theNewId == null) { 097 return; 098 } 099 String newUnqualifiedVersionLessId = theNewId.toUnqualifiedVersionless().getValue(); 100 entrySet().stream() 101 .map(Pair::getValue) 102 .filter(targetId -> 103 Objects.equals(targetId.toUnqualifiedVersionless().getValue(), newUnqualifiedVersionLessId)) 104 .forEach(targetId -> targetId.setValue(theNewId.getValue())); 105 } 106 107 private static class Entry { 108 109 private final String myUnversionedId; 110 private final IIdType myId; 111 112 private Entry(String theId) { 113 myId = null; 114 myUnversionedId = theId; 115 } 116 117 private Entry(IIdType theId) { 118 String unversionedId = toVersionlessValue(theId); 119 myUnversionedId = unversionedId; 120 myId = theId; 121 } 122 123 @Override 124 public boolean equals(Object theOther) { 125 if (theOther instanceof Entry) { 126 String otherUnversionedId = ((Entry) theOther).myUnversionedId; 127 if (myUnversionedId.equals(otherUnversionedId)) { 128 return true; 129 } 130 } 131 return false; 132 } 133 134 @Override 135 public int hashCode() { 136 return myUnversionedId.hashCode(); 137 } 138 } 139 140 static String toVersionlessValue(IIdType theId) { 141 boolean isPlaceholder = theId.getValue().startsWith("urn:"); 142 String unversionedId; 143 if (isPlaceholder || (!theId.hasBaseUrl() && !theId.hasVersionIdPart()) || !theId.hasResourceType()) { 144 unversionedId = theId.getValue(); 145 } else { 146 unversionedId = theId.toUnqualifiedVersionless().getValue(); 147 } 148 return unversionedId; 149 } 150}