
001package ca.uhn.fhir.util; 002 003/*- 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2022 Smile CDR, Inc. 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023 024import ca.uhn.fhir.context.FhirContext; 025import ca.uhn.fhir.i18n.Msg; 026import org.hl7.fhir.instance.model.api.IBase; 027import org.hl7.fhir.instance.model.api.IBaseDatatype; 028import org.hl7.fhir.instance.model.api.IBaseExtension; 029import org.hl7.fhir.instance.model.api.IBaseHasExtensions; 030 031import java.util.List; 032import java.util.function.Predicate; 033import java.util.stream.Collectors; 034 035/** 036 * Utility for modifying with extensions in a FHIR version-independent approach. 037 */ 038public class ExtensionUtil { 039 040 /** 041 * Non instantiable 042 */ 043 private ExtensionUtil() { 044 // nothing 045 } 046 047 /** 048 * Returns an extension with the specified URL creating one if it doesn't exist. 049 * 050 * @param theBase Base resource to get extension from 051 * @param theUrl URL for the extension 052 * @return Returns a extension with the specified URL. 053 * @throws IllegalArgumentException IllegalArgumentException is thrown in case resource doesn't support extensions 054 */ 055 public static IBaseExtension<?, ?> getOrCreateExtension(IBase theBase, String theUrl) { 056 IBaseHasExtensions baseHasExtensions = validateExtensionSupport(theBase); 057 IBaseExtension<?,?> extension = getExtensionByUrl(baseHasExtensions, theUrl); 058 if (extension == null) { 059 extension = baseHasExtensions.addExtension(); 060 extension.setUrl(theUrl); 061 } 062 return extension; 063 } 064 065 /** 066 * Returns an new empty extension. 067 * 068 * @param theBase Base resource to add the extension to 069 * @return Returns a new extension 070 * @throws IllegalArgumentException IllegalArgumentException is thrown in case resource doesn't support extensions 071 */ 072 public static IBaseExtension<?, ?> addExtension(IBase theBase) { 073 return addExtension(theBase, null); 074 } 075 076 /** 077 * Returns an extension with the specified URL 078 * 079 * @param theBase Base resource to add the extension to 080 * @param theUrl URL for the extension 081 * @return Returns a new extension with the specified URL. 082 * @throws IllegalArgumentException IllegalArgumentException is thrown in case resource doesn't support extensions 083 */ 084 public static IBaseExtension<?, ?> addExtension(IBase theBase, String theUrl) { 085 IBaseHasExtensions baseHasExtensions = validateExtensionSupport(theBase); 086 IBaseExtension<?,?> extension = baseHasExtensions.addExtension(); 087 if (theUrl != null) { 088 extension.setUrl(theUrl); 089 } 090 return extension; 091 } 092 093 /** 094 * Adds an extension with the specified value 095 * 096 * @param theBase The resource to update extension on 097 * @param theUrl Extension URL 098 * @param theValueType Type of the value to set in the extension 099 * @param theValue Extension value 100 * @param theFhirContext The context containing FHIR resource definitions 101 */ 102 public static void addExtension(FhirContext theFhirContext, IBase theBase, String theUrl, String theValueType, Object theValue) { 103 IBaseExtension<?,?> ext = addExtension(theBase, theUrl); 104 setExtension(theFhirContext, ext, theValueType, theValue); 105 } 106 107 private static IBaseHasExtensions validateExtensionSupport(IBase theBase) { 108 if (!(theBase instanceof IBaseHasExtensions)) { 109 throw new IllegalArgumentException(Msg.code(1747) + String.format("Expected instance that supports extensions, but got %s", theBase)); 110 } 111 return (IBaseHasExtensions) theBase; 112 } 113 114 /** 115 * Checks if the specified instance has an extension with the specified URL 116 * 117 * @param theBase The base resource to check extensions on 118 * @param theExtensionUrl URL of the extension 119 * @return Returns true if extension is exists and false otherwise 120 */ 121 public static boolean hasExtension(IBase theBase, String theExtensionUrl) { 122 IBaseHasExtensions baseHasExtensions; 123 try { 124 baseHasExtensions = validateExtensionSupport(theBase); 125 } catch (Exception e) { 126 return false; 127 } 128 129 return getExtensionByUrl(baseHasExtensions, theExtensionUrl) != null; 130 } 131 132 /** 133 * Checks if the specified instance has an extension with the specified URL 134 * 135 * @param theBase The base resource to check extensions on 136 * @param theExtensionUrl URL of the extension 137 * @return Returns true if extension is exists and false otherwise 138 */ 139 public static boolean hasExtension(IBase theBase, String theExtensionUrl, String theExtensionValue) { 140 if (!hasExtension(theBase, theExtensionUrl)) { 141 return false; 142 } 143 IBaseDatatype value = getExtensionByUrl(theBase, theExtensionUrl).getValue(); 144 if (value == null) { 145 return theExtensionValue == null; 146 } 147 return value.toString().equals(theExtensionValue); 148 } 149 150 /** 151 * Gets the first extension with the specified URL 152 * 153 * @param theBase The resource to get the extension for 154 * @param theExtensionUrl URL of the extension to get. Must be non-null 155 * @return Returns the first available extension with the specified URL, or null if such extension doesn't exist 156 */ 157 public static IBaseExtension<?, ?> getExtensionByUrl(IBase theBase, String theExtensionUrl) { 158 Predicate<IBaseExtension<?,?>> filter; 159 if (theExtensionUrl == null) { 160 filter = (e -> true); 161 } else { 162 filter = (e -> theExtensionUrl.equals(e.getUrl())); 163 } 164 165 return getExtensionsMatchingPredicate(theBase, filter) 166 .stream() 167 .findFirst() 168 .orElse(null); 169 } 170 171 /** 172 * Gets all extensions that match the specified filter predicate 173 * 174 * @param theBase The resource to get the extension for 175 * @param theFilter Predicate to match the extension against 176 * @return Returns all extension with the specified URL, or an empty list if such extensions do not exist 177 */ 178 public static List<IBaseExtension<?, ?>> getExtensionsMatchingPredicate(IBase theBase, Predicate<? super IBaseExtension<?,?>> theFilter) { 179 return validateExtensionSupport(theBase) 180 .getExtension() 181 .stream() 182 .filter(theFilter) 183 .collect(Collectors.toList()); 184 } 185 186 /** 187 * Removes all extensions. 188 * 189 * @param theBase The resource to clear the extension for 190 * @return Returns all extension that were removed 191 */ 192 public static List<IBaseExtension<?, ?>> clearAllExtensions(IBase theBase) { 193 return clearExtensionsMatchingPredicate(theBase, (e -> true)); 194 } 195 196 /** 197 * Removes all extensions by URL. 198 * 199 * @param theBase The resource to clear the extension for 200 * @param theUrl The url to clear extensions for 201 * @return Returns all extension that were removed 202 */ 203 public static List<IBaseExtension<?, ?>> clearExtensionsByUrl(IBase theBase, String theUrl) { 204 return clearExtensionsMatchingPredicate(theBase, (e -> theUrl.equals(e.getUrl()))); 205 } 206 207 /** 208 * Removes all extensions that match the specified predicate 209 * 210 * @param theBase The base object to clear the extension for 211 * @param theFilter Defines which extensions should be cleared 212 * @return Returns all extension that were removed 213 */ 214 private static List<IBaseExtension<?, ?>> clearExtensionsMatchingPredicate(IBase theBase, Predicate<? super IBaseExtension<?,?>> theFilter) { 215 List<IBaseExtension<?, ?>> retVal = getExtensionsMatchingPredicate(theBase, theFilter); 216 validateExtensionSupport(theBase) 217 .getExtension() 218 .removeIf(theFilter); 219 return retVal; 220 } 221 222 /** 223 * Gets all extensions with the specified URL 224 * 225 * @param theBase The resource to get the extension for 226 * @param theExtensionUrl URL of the extension to get. Must be non-null 227 * @return Returns all extension with the specified URL, or an empty list if such extensions do not exist 228 */ 229 public static List<IBaseExtension<?, ?>> getExtensionsByUrl(IBaseHasExtensions theBase, String theExtensionUrl) { 230 Predicate<IBaseExtension<?,?>> urlEqualityPredicate = e -> theExtensionUrl.equals(e.getUrl()); 231 return getExtensionsMatchingPredicate(theBase, urlEqualityPredicate); 232 } 233 234 /** 235 * Sets value of the extension as a string 236 * 237 * @param theExtension The extension to set the value on 238 * @param theValue The value to set 239 * @param theFhirContext The context containing FHIR resource definitions 240 */ 241 public static void setExtension(FhirContext theFhirContext, IBaseExtension<?,?> theExtension, String theValue) { 242 setExtension(theFhirContext, theExtension, "string", theValue); 243 } 244 245 /** 246 * Sets value of the extension 247 * 248 * @param theExtension The extension to set the value on 249 * @param theExtensionType Element type of the extension 250 * @param theValue The value to set 251 * @param theFhirContext The context containing FHIR resource definitions 252 */ 253 public static void setExtension(FhirContext theFhirContext, IBaseExtension<?,?> theExtension, String theExtensionType, Object theValue) { 254 theExtension.setValue(TerserUtil.newElement(theFhirContext, theExtensionType, theValue)); 255 } 256 257 /** 258 * Sets or replaces existing extension with the specified value as a string 259 * 260 * @param theBase The resource to update extension on 261 * @param theUrl Extension URL 262 * @param theValue Extension value 263 * @param theFhirContext The context containing FHIR resource definitions 264 */ 265 public static void setExtensionAsString(FhirContext theFhirContext, IBase theBase, String theUrl, String theValue) { 266 IBaseExtension<?,?> ext = getOrCreateExtension(theBase, theUrl); 267 setExtension(theFhirContext, ext, theValue); 268 } 269 270 /** 271 * Sets or replaces existing extension with the specified value 272 * 273 * @param theBase The resource to update extension on 274 * @param theUrl Extension URL 275 * @param theValueType Type of the value to set in the extension 276 * @param theValue Extension value 277 * @param theFhirContext The context containing FHIR resource definitions 278 */ 279 public static void setExtension(FhirContext theFhirContext, IBase theBase, String theUrl, String theValueType, Object theValue) { 280 IBaseExtension<?,?> ext = getOrCreateExtension(theBase, theUrl); 281 setExtension(theFhirContext, ext, theValueType, theValue); 282 } 283 284 /** 285 * Compares two extensions, returns true if they have the same value and url 286 * 287 * @param theLeftExtension : Extension to be evaluated #1 288 * @param theRightExtension : Extension to be evaluated #2 289 * @return Result of the comparison 290 */ 291 public static boolean equals(IBaseExtension<?,?> theLeftExtension, IBaseExtension<?,?> theRightExtension) { 292 return TerserUtil.equals(theLeftExtension, theRightExtension); 293 } 294}