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