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 023import ca.uhn.fhir.context.BaseRuntimeChildDefinition; 024import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; 025import ca.uhn.fhir.context.BaseRuntimeElementDefinition; 026import ca.uhn.fhir.context.FhirContext; 027import ca.uhn.fhir.context.RuntimeChildChoiceDefinition; 028import ca.uhn.fhir.context.RuntimeResourceDefinition; 029import ca.uhn.fhir.i18n.Msg; 030import org.apache.commons.lang3.Validate; 031import org.apache.commons.lang3.tuple.Triple; 032import org.hl7.fhir.instance.model.api.IBase; 033import org.hl7.fhir.instance.model.api.IBaseResource; 034import org.hl7.fhir.instance.model.api.IPrimitiveType; 035import org.slf4j.Logger; 036 037import java.lang.reflect.Method; 038import java.util.Arrays; 039import java.util.Collection; 040import java.util.Collections; 041import java.util.List; 042import java.util.function.Predicate; 043import java.util.stream.Collectors; 044import java.util.stream.Stream; 045 046import static org.slf4j.LoggerFactory.getLogger; 047 048public final class TerserUtil { 049 050 public static final String FIELD_NAME_IDENTIFIER = "identifier"; 051 /** 052 * Exclude for id, identifier and meta fields of a resource. 053 */ 054 public static final Collection<String> IDS_AND_META_EXCLUDES = 055 Collections.unmodifiableSet(Stream.of("id", "identifier", "meta").collect(Collectors.toSet())); 056 /** 057 * Exclusion predicate for id, identifier, meta fields. 058 */ 059 public static final Predicate<String> EXCLUDE_IDS_AND_META = new Predicate<String>() { 060 @Override 061 public boolean test(String s) { 062 return !IDS_AND_META_EXCLUDES.contains(s); 063 } 064 }; 065 /** 066 * Exclusion predicate for id/identifier, meta and fields with empty values. This ensures that source / target resources, 067 * empty source fields will not results in erasure of target fields. 068 */ 069 public static final Predicate<Triple<BaseRuntimeChildDefinition, IBase, IBase>> EXCLUDE_IDS_META_AND_EMPTY = new Predicate<Triple<BaseRuntimeChildDefinition, IBase, IBase>>() { 070 @Override 071 public boolean test(Triple<BaseRuntimeChildDefinition, IBase, IBase> theTriple) { 072 if (!EXCLUDE_IDS_AND_META.test(theTriple.getLeft().getElementName())) { 073 return false; 074 } 075 BaseRuntimeChildDefinition childDefinition = theTriple.getLeft(); 076 boolean isSourceFieldEmpty = childDefinition.getAccessor().getValues(theTriple.getMiddle()).isEmpty(); 077 return !isSourceFieldEmpty; 078 } 079 }; 080 /** 081 * Exclusion predicate for keeping all fields. 082 */ 083 public static final Predicate<String> INCLUDE_ALL = new Predicate<String>() { 084 @Override 085 public boolean test(String s) { 086 return true; 087 } 088 }; 089 private static final Logger ourLog = getLogger(TerserUtil.class); 090 private static final String EQUALS_DEEP = "equalsDeep"; 091 092 private TerserUtil() { 093 } 094 095 /** 096 * Given an Child Definition of `identifier`, a R4/DSTU3 EID Identifier, and a new resource, clone the EID into that resources' identifier list. 097 */ 098 public static void cloneEidIntoResource(FhirContext theFhirContext, BaseRuntimeChildDefinition theIdentifierDefinition, IBase theEid, IBase theResourceToCloneEidInto) { 099 // FHIR choice types - fields within fhir where we have a choice of ids 100 BaseRuntimeElementCompositeDefinition<?> childIdentifier = (BaseRuntimeElementCompositeDefinition<?>) theIdentifierDefinition.getChildByName(FIELD_NAME_IDENTIFIER); 101 IBase resourceNewIdentifier = childIdentifier.newInstance(); 102 103 FhirTerser terser = theFhirContext.newTerser(); 104 terser.cloneInto(theEid, resourceNewIdentifier, true); 105 theIdentifierDefinition.getMutator().addValue(theResourceToCloneEidInto, resourceNewIdentifier); 106 } 107 108 /** 109 * Checks if the specified fields has any values 110 * 111 * @param theFhirContext Context holding resource definition 112 * @param theResource Resource to check if the specified field is set 113 * @param theFieldName name of the field to check 114 * @return Returns true if field exists and has any values set, and false otherwise 115 */ 116 public static boolean hasValues(FhirContext theFhirContext, IBaseResource theResource, String theFieldName) { 117 RuntimeResourceDefinition resourceDefinition = theFhirContext.getResourceDefinition(theResource); 118 BaseRuntimeChildDefinition resourceIdentifier = resourceDefinition.getChildByName(theFieldName); 119 if (resourceIdentifier == null) { 120 return false; 121 } 122 return !(resourceIdentifier.getAccessor().getValues(theResource).isEmpty()); 123 } 124 125 /** 126 * Gets all values of the specified field. 127 * 128 * @param theFhirContext Context holding resource definition 129 * @param theResource Resource to check if the specified field is set 130 * @param theFieldName name of the field to check 131 * @return Returns all values for the specified field or null if field with the provided name doesn't exist 132 */ 133 public static List<IBase> getValues(FhirContext theFhirContext, IBaseResource theResource, String theFieldName) { 134 RuntimeResourceDefinition resourceDefinition = theFhirContext.getResourceDefinition(theResource); 135 BaseRuntimeChildDefinition resourceIdentifier = resourceDefinition.getChildByName(theFieldName); 136 if (resourceIdentifier == null) { 137 ourLog.info("There is no field named {} in Resource {}", theFieldName, resourceDefinition.getName()); 138 return null; 139 } 140 return resourceIdentifier.getAccessor().getValues(theResource); 141 } 142 143 /** 144 * Gets the first available value for the specified field. 145 * 146 * @param theFhirContext Context holding resource definition 147 * @param theResource Resource to check if the specified field is set 148 * @param theFieldName name of the field to check 149 * @return Returns the first value for the specified field or null if field with the provided name doesn't exist or 150 * has no values 151 */ 152 public static IBase getValueFirstRep(FhirContext theFhirContext, IBaseResource theResource, String theFieldName) { 153 List<IBase> values = getValues(theFhirContext, theResource, theFieldName); 154 if (values == null || values.isEmpty()) { 155 return null; 156 } 157 return values.get(0); 158 } 159 160 /** 161 * Clones specified composite field (collection). Composite field values must conform to the collections 162 * contract. 163 * 164 * @param theFrom Resource to clone the specified field from 165 * @param theTo Resource to clone the specified field to 166 * @param theField Field name to be copied 167 */ 168 public static void cloneCompositeField(FhirContext theFhirContext, IBaseResource theFrom, IBaseResource theTo, String theField) { 169 FhirTerser terser = theFhirContext.newTerser(); 170 171 RuntimeResourceDefinition definition = theFhirContext.getResourceDefinition(theFrom); 172 BaseRuntimeChildDefinition childDefinition = definition.getChildByName(theField); 173 Validate.notNull(childDefinition); 174 175 List<IBase> theFromFieldValues = childDefinition.getAccessor().getValues(theFrom); 176 List<IBase> theToFieldValues = childDefinition.getAccessor().getValues(theTo); 177 178 for (IBase theFromFieldValue : theFromFieldValues) { 179 if (containsPrimitiveValue(theFromFieldValue, theToFieldValues)) { 180 continue; 181 } 182 183 IBase newFieldValue = newElement(terser, childDefinition, theFromFieldValue, null); 184 terser.cloneInto(theFromFieldValue, newFieldValue, true); 185 186 try { 187 theToFieldValues.add(newFieldValue); 188 } catch (Exception e) { 189 childDefinition.getMutator().setValue(theTo, newFieldValue); 190 } 191 } 192 } 193 194 private static boolean containsPrimitiveValue(IBase theItem, List<IBase> theItems) { 195 PrimitiveTypeEqualsPredicate predicate = new PrimitiveTypeEqualsPredicate(); 196 return theItems.stream().anyMatch(i -> { 197 return predicate.test(i, theItem); 198 }); 199 } 200 201 private static Method getMethod(IBase theBase, String theMethodName) { 202 Method method = null; 203 for (Method m : theBase.getClass().getDeclaredMethods()) { 204 if (m.getName().equals(theMethodName)) { 205 method = m; 206 break; 207 } 208 } 209 return method; 210 } 211 212 /** 213 * Checks if two items are equal via {@link #EQUALS_DEEP} method 214 * 215 * @param theItem1 First item to compare 216 * @param theItem2 Second item to compare 217 * @return Returns true if they are equal and false otherwise 218 */ 219 public static boolean equals(IBase theItem1, IBase theItem2) { 220 if (theItem1 == null) { 221 return theItem2 == null; 222 } 223 224 final Method method = getMethod(theItem1, EQUALS_DEEP); 225 Validate.notNull(method); 226 return equals(theItem1, theItem2, method); 227 } 228 229 private static boolean equals(IBase theItem1, IBase theItem2, Method theMethod) { 230 if (theMethod != null) { 231 try { 232 return (Boolean) theMethod.invoke(theItem1, theItem2); 233 } catch (Exception e) { 234 throw new RuntimeException(Msg.code(1746) + String.format("Unable to compare equality via %s", EQUALS_DEEP), e); 235 } 236 } 237 return theItem1.equals(theItem2); 238 } 239 240 private static boolean contains(IBase theItem, List<IBase> theItems) { 241 final Method method = getMethod(theItem, EQUALS_DEEP); 242 return theItems.stream().anyMatch(i -> equals(i, theItem, method)); 243 } 244 245 /** 246 * Merges all fields on the provided instance. <code>theTo</code> will contain a union of all values from <code>theFrom</code> 247 * instance and <code>theTo</code> instance. 248 * 249 * @param theFhirContext Context holding resource definition 250 * @param theFrom The resource to merge the fields from 251 * @param theTo The resource to merge the fields into 252 */ 253 public static void mergeAllFields(FhirContext theFhirContext, IBaseResource theFrom, IBaseResource theTo) { 254 mergeFields(theFhirContext, theFrom, theTo, INCLUDE_ALL); 255 } 256 257 /** 258 * Replaces all fields that have matching field names by the given inclusion strategy. <code>theTo</code> will contain a copy of the 259 * values from <code>theFrom</code> instance. 260 * 261 * @param theFhirContext Context holding resource definition 262 * @param theFrom The resource to merge the fields from 263 * @param theTo The resource to merge the fields into 264 * @param theFieldNameInclusion Inclusion strategy that checks if a given field should be replaced 265 */ 266 public static void replaceFields(FhirContext theFhirContext, IBaseResource theFrom, IBaseResource theTo, Predicate<String> theFieldNameInclusion) { 267 Predicate<Triple<BaseRuntimeChildDefinition, IBase, IBase>> predicate 268 = (t) -> theFieldNameInclusion.test(t.getLeft().getElementName()); 269 replaceFieldsByPredicate(theFhirContext, theFrom, theTo, predicate); 270 } 271 272 /** 273 * Replaces fields on theTo resource that test positive by the given predicate. <code>theTo</code> will contain a copy of the 274 * values from <code>theFrom</code> for which predicate tests positive. Please note that composite fields will be replaced fully. 275 * 276 * @param theFhirContext Context holding resource definition 277 * @param theFrom The resource to merge the fields from 278 * @param theTo The resource to merge the fields into 279 * @param thePredicate Predicate that checks if a given field should be replaced 280 */ 281 public static void replaceFieldsByPredicate(FhirContext theFhirContext, IBaseResource theFrom, IBaseResource theTo, Predicate<Triple<BaseRuntimeChildDefinition, IBase, IBase>> thePredicate) { 282 RuntimeResourceDefinition definition = theFhirContext.getResourceDefinition(theFrom); 283 FhirTerser terser = theFhirContext.newTerser(); 284 for (BaseRuntimeChildDefinition childDefinition : definition.getChildrenAndExtension()) { 285 if (thePredicate.test(Triple.of(childDefinition, theFrom, theTo))) { 286 replaceField(terser, theFrom, theTo, childDefinition); 287 } 288 } 289 } 290 291 /** 292 * Checks if the field exists on the resource 293 * 294 * @param theFhirContext Context holding resource definition 295 * @param theFieldName Name of the field to check 296 * @param theInstance Resource instance to check 297 * @return Returns true if resource definition has a child with the specified name and false otherwise 298 */ 299 public static boolean fieldExists(FhirContext theFhirContext, String theFieldName, IBaseResource theInstance) { 300 RuntimeResourceDefinition definition = theFhirContext.getResourceDefinition(theInstance); 301 return definition.getChildByName(theFieldName) != null; 302 } 303 304 /** 305 * Replaces the specified fields on <code>theTo</code> resource with the value from <code>theFrom</code> resource. 306 * 307 * @param theFhirContext Context holding resource definition 308 * @param theFrom The resource to replace the field from 309 * @param theTo The resource to replace the field on 310 */ 311 public static void replaceField(FhirContext theFhirContext, String theFieldName, IBaseResource theFrom, IBaseResource theTo) { 312 RuntimeResourceDefinition definition = theFhirContext.getResourceDefinition(theFrom); 313 Validate.notNull(definition); 314 replaceField(theFhirContext.newTerser(), theFrom, theTo, theFhirContext.getResourceDefinition(theFrom).getChildByName(theFieldName)); 315 } 316 317 /** 318 * Clears the specified field on the resource provided 319 * 320 * @param theFhirContext Context holding resource definition 321 * @param theResource 322 * @param theFieldName 323 */ 324 public static void clearField(FhirContext theFhirContext, IBaseResource theResource, String theFieldName) { 325 BaseRuntimeChildDefinition childDefinition = getBaseRuntimeChildDefinition(theFhirContext, theFieldName, theResource); 326 clear(childDefinition.getAccessor().getValues(theResource)); 327 } 328 329 /** 330 * Clears the specified field on the element provided 331 * 332 * @param theFhirContext Context holding resource definition 333 * @param theFieldName Name of the field to clear values for 334 * @param theBase The element definition to clear values on 335 */ 336 public static void clearField(FhirContext theFhirContext, String theFieldName, IBase theBase) { 337 BaseRuntimeElementDefinition definition = theFhirContext.getElementDefinition(theBase.getClass()); 338 BaseRuntimeChildDefinition childDefinition = definition.getChildByName(theFieldName); 339 Validate.notNull(childDefinition); 340 clear(childDefinition.getAccessor().getValues(theBase)); 341 } 342 343 /** 344 * Sets the provided field with the given values. This method will add to the collection of existing field values 345 * in case of multiple cardinality. Use {@link #clearField(FhirContext, IBaseResource, String)} 346 * to remove values before setting 347 * 348 * @param theFhirContext Context holding resource definition 349 * @param theFieldName Child field name of the resource to set 350 * @param theResource The resource to set the values on 351 * @param theValues The values to set on the resource child field name 352 */ 353 public static void setField(FhirContext theFhirContext, String theFieldName, IBaseResource theResource, IBase... theValues) { 354 setField(theFhirContext, theFhirContext.newTerser(), theFieldName, theResource, theValues); 355 } 356 357 /** 358 * Sets the provided field with the given values. This method will add to the collection of existing field values 359 * in case of multiple cardinality. Use {@link #clearField(FhirContext, IBaseResource, String)} 360 * to remove values before setting 361 * 362 * @param theFhirContext Context holding resource definition 363 * @param theTerser Terser to be used when cloning field values 364 * @param theFieldName Child field name of the resource to set 365 * @param theResource The resource to set the values on 366 * @param theValues The values to set on the resource child field name 367 */ 368 public static void setField(FhirContext theFhirContext, FhirTerser theTerser, String theFieldName, IBaseResource theResource, IBase... theValues) { 369 BaseRuntimeChildDefinition childDefinition = getBaseRuntimeChildDefinition(theFhirContext, theFieldName, theResource); 370 List<IBase> theFromFieldValues = childDefinition.getAccessor().getValues(theResource); 371 if (theFromFieldValues.isEmpty()) { 372 for (IBase value : theValues) { 373 try { 374 childDefinition.getMutator().addValue(theResource, value); 375 } catch (UnsupportedOperationException e) { 376 ourLog.warn("Resource {} does not support multiple values, but an attempt to set {} was made. Setting the first item only", theResource, theValues); 377 childDefinition.getMutator().setValue(theResource, value); 378 break; 379 } 380 } 381 return; 382 } 383 List<IBase> theToFieldValues = Arrays.asList(theValues); 384 mergeFields(theTerser, theResource, childDefinition, theFromFieldValues, theToFieldValues); 385 } 386 387 /** 388 * Sets the specified value at the FHIR path provided. 389 * 390 * @param theTerser The terser that should be used for cloning the field value. 391 * @param theFhirPath The FHIR path to set the field at 392 * @param theResource The resource on which the value should be set 393 * @param theValue The value to set 394 */ 395 public static void setFieldByFhirPath(FhirTerser theTerser, String theFhirPath, IBaseResource theResource, IBase theValue) { 396 List<IBase> theFromFieldValues = theTerser.getValues(theResource, theFhirPath, true, false); 397 for (IBase theFromFieldValue : theFromFieldValues) { 398 theTerser.cloneInto(theValue, theFromFieldValue, true); 399 } 400 } 401 402 /** 403 * Sets the specified value at the FHIR path provided. 404 * 405 * @param theFhirContext Context holding resource definition 406 * @param theFhirPath The FHIR path to set the field at 407 * @param theResource The resource on which the value should be set 408 * @param theValue The value to set 409 */ 410 public static void setFieldByFhirPath(FhirContext theFhirContext, String theFhirPath, IBaseResource theResource, IBase theValue) { 411 setFieldByFhirPath(theFhirContext.newTerser(), theFhirPath, theResource, theValue); 412 } 413 414 /** 415 * Returns field values ant the specified FHIR path from the resource. 416 * 417 * @param theFhirContext Context holding resource definition 418 * @param theFhirPath The FHIR path to get the field from 419 * @param theResource The resource from which the value should be retrieved 420 * @return Returns the list of field values at the given FHIR path 421 */ 422 public static List<IBase> getFieldByFhirPath(FhirContext theFhirContext, String theFhirPath, IBase theResource) { 423 return theFhirContext.newTerser().getValues(theResource, theFhirPath, false, false); 424 } 425 426 /** 427 * Returns the first available field value at the specified FHIR path from the resource. 428 * 429 * @param theFhirContext Context holding resource definition 430 * @param theFhirPath The FHIR path to get the field from 431 * @param theResource The resource from which the value should be retrieved 432 * @return Returns the first available value or null if no values can be retrieved 433 */ 434 public static IBase getFirstFieldByFhirPath(FhirContext theFhirContext, String theFhirPath, IBase theResource) { 435 List<IBase> values = getFieldByFhirPath(theFhirContext, theFhirPath, theResource); 436 if (values == null || values.isEmpty()) { 437 return null; 438 } 439 return values.get(0); 440 } 441 442 private static void replaceField(FhirTerser theTerser, IBaseResource theFrom, IBaseResource theTo, BaseRuntimeChildDefinition childDefinition) { 443 List<IBase> fromValues = childDefinition.getAccessor().getValues(theFrom); 444 List<IBase> toValues = childDefinition.getAccessor().getValues(theTo); 445 if (fromValues != toValues) { 446 clear(toValues); 447 448 mergeFields(theTerser, theTo, childDefinition, fromValues, toValues); 449 } 450 } 451 452 /** 453 * Merges values of all fields except for "identifier" and "meta" from <code>theFrom</code> resource to 454 * <code>theTo</code> resource. Fields values are compared via the equalsDeep method, or via object identity if this 455 * method is not available. 456 * 457 * @param theFhirContext Context holding resource definition 458 * @param theFrom Resource to merge the specified field from 459 * @param theTo Resource to merge the specified field into 460 */ 461 public static void mergeFieldsExceptIdAndMeta(FhirContext theFhirContext, IBaseResource theFrom, IBaseResource theTo) { 462 mergeFields(theFhirContext, theFrom, theTo, EXCLUDE_IDS_AND_META); 463 } 464 465 /** 466 * Merges values of all field from <code>theFrom</code> resource to <code>theTo</code> resource. Fields 467 * values are compared via the equalsDeep method, or via object identity if this method is not available. 468 * 469 * @param theFhirContext Context holding resource definition 470 * @param theFrom Resource to merge the specified field from 471 * @param theTo Resource to merge the specified field into 472 * @param inclusionStrategy Predicate to test which fields should be merged 473 */ 474 public static void mergeFields(FhirContext theFhirContext, IBaseResource theFrom, IBaseResource theTo, Predicate<String> inclusionStrategy) { 475 FhirTerser terser = theFhirContext.newTerser(); 476 477 RuntimeResourceDefinition definition = theFhirContext.getResourceDefinition(theFrom); 478 for (BaseRuntimeChildDefinition childDefinition : definition.getChildrenAndExtension()) { 479 if (!inclusionStrategy.test(childDefinition.getElementName())) { 480 continue; 481 } 482 483 List<IBase> theFromFieldValues = childDefinition.getAccessor().getValues(theFrom); 484 List<IBase> theToFieldValues = childDefinition.getAccessor().getValues(theTo); 485 486 mergeFields(terser, theTo, childDefinition, theFromFieldValues, theToFieldValues); 487 } 488 } 489 490 /** 491 * Merges value of the specified field from <code>theFrom</code> resource to <code>theTo</code> resource. Fields 492 * values are compared via the equalsDeep method, or via object identity if this method is not available. 493 * 494 * @param theFhirContext Context holding resource definition 495 * @param theFieldName Name of the child filed to merge 496 * @param theFrom Resource to merge the specified field from 497 * @param theTo Resource to merge the specified field into 498 */ 499 public static void mergeField(FhirContext theFhirContext, String theFieldName, IBaseResource theFrom, IBaseResource theTo) { 500 mergeField(theFhirContext, theFhirContext.newTerser(), theFieldName, theFrom, theTo); 501 } 502 503 /** 504 * Merges value of the specified field from <code>theFrom</code> resource to <code>theTo</code> resource. Fields 505 * values are compared via the equalsDeep method, or via object identity if this method is not available. 506 * 507 * @param theFhirContext Context holding resource definition 508 * @param theTerser Terser to be used when cloning the field values 509 * @param theFieldName Name of the child filed to merge 510 * @param theFrom Resource to merge the specified field from 511 * @param theTo Resource to merge the specified field into 512 */ 513 public static void mergeField(FhirContext theFhirContext, FhirTerser theTerser, String theFieldName, IBaseResource theFrom, IBaseResource theTo) { 514 BaseRuntimeChildDefinition childDefinition = getBaseRuntimeChildDefinition(theFhirContext, theFieldName, theFrom); 515 516 List<IBase> theFromFieldValues = childDefinition.getAccessor().getValues(theFrom); 517 List<IBase> theToFieldValues = childDefinition.getAccessor().getValues(theTo); 518 519 mergeFields(theTerser, theTo, childDefinition, theFromFieldValues, theToFieldValues); 520 } 521 522 private static BaseRuntimeChildDefinition getBaseRuntimeChildDefinition(FhirContext theFhirContext, String theFieldName, IBaseResource theFrom) { 523 RuntimeResourceDefinition definition = theFhirContext.getResourceDefinition(theFrom); 524 BaseRuntimeChildDefinition childDefinition = definition.getChildByName(theFieldName); 525 Validate.notNull(childDefinition); 526 return childDefinition; 527 } 528 529 /** 530 * Creates a new element taking into consideration elements with choice that are not directly retrievable by element 531 * name 532 * 533 * 534 * @param theFhirTerser 535 * @param theChildDefinition Child to create a new instance for 536 * @param theFromFieldValue The base parent field 537 * @param theConstructorParam Optional constructor param 538 * @return Returns the new element with the given value if configured 539 */ 540 private static IBase newElement(FhirTerser theFhirTerser, BaseRuntimeChildDefinition theChildDefinition, IBase theFromFieldValue, Object theConstructorParam) { 541 BaseRuntimeElementDefinition runtimeElementDefinition; 542 if (theChildDefinition instanceof RuntimeChildChoiceDefinition) { 543 runtimeElementDefinition = theChildDefinition.getChildElementDefinitionByDatatype(theFromFieldValue.getClass()); 544 } else { 545 runtimeElementDefinition = theChildDefinition.getChildByName(theChildDefinition.getElementName()); 546 } 547 if ("contained".equals(runtimeElementDefinition.getName())) { 548 IBaseResource sourceResource = (IBaseResource) theFromFieldValue; 549 return theFhirTerser.clone(sourceResource); 550 } else if (theConstructorParam == null) { 551 return runtimeElementDefinition.newInstance(); 552 } else { 553 return runtimeElementDefinition.newInstance(theConstructorParam); 554 } 555 } 556 557 private static void mergeFields(FhirTerser theTerser, IBaseResource theTo, BaseRuntimeChildDefinition childDefinition, List<IBase> theFromFieldValues, List<IBase> theToFieldValues) { 558 for (IBase theFromFieldValue : theFromFieldValues) { 559 if (contains(theFromFieldValue, theToFieldValues)) { 560 continue; 561 } 562 563 IBase newFieldValue = newElement(theTerser, childDefinition, theFromFieldValue, null); 564 if (theFromFieldValue instanceof IPrimitiveType) { 565 try { 566 Method copyMethod = getMethod(theFromFieldValue, "copy"); 567 if (copyMethod != null) { 568 newFieldValue = (IBase) copyMethod.invoke(theFromFieldValue, new Object[]{}); 569 } 570 } catch (Throwable t) { 571 ((IPrimitiveType) newFieldValue).setValueAsString(((IPrimitiveType) theFromFieldValue).getValueAsString()); 572 } 573 } else { 574 theTerser.cloneInto(theFromFieldValue, newFieldValue, true); 575 } 576 577 try { 578 theToFieldValues.add(newFieldValue); 579 } catch (UnsupportedOperationException e) { 580 childDefinition.getMutator().setValue(theTo, newFieldValue); 581 theToFieldValues = childDefinition.getAccessor().getValues(theTo); 582 } 583 } 584 } 585 586 /** 587 * Clones the specified resource. 588 * 589 * @param theFhirContext Context holding resource definition 590 * @param theInstance The instance to be cloned 591 * @param <T> Base resource type 592 * @return Returns a cloned instance 593 */ 594 public static <T extends IBaseResource> T clone(FhirContext theFhirContext, T theInstance) { 595 RuntimeResourceDefinition definition = theFhirContext.getResourceDefinition(theInstance.getClass()); 596 T retVal = (T) definition.newInstance(); 597 598 FhirTerser terser = theFhirContext.newTerser(); 599 terser.cloneInto(theInstance, retVal, true); 600 return retVal; 601 } 602 603 /** 604 * Creates a new element instance 605 * 606 * @param theFhirContext Context holding resource definition 607 * @param theElementType Element type name 608 * @param <T> Base element type 609 * @return Returns a new instance of the element 610 */ 611 public static <T extends IBase> T newElement(FhirContext theFhirContext, String theElementType) { 612 BaseRuntimeElementDefinition def = theFhirContext.getElementDefinition(theElementType); 613 return (T) def.newInstance(); 614 } 615 616 /** 617 * Creates a new element instance 618 * 619 * @param theFhirContext Context holding resource definition 620 * @param theElementType Element type name 621 * @param theConstructorParam Initialization parameter for the element 622 * @param <T> Base element type 623 * @return Returns a new instance of the element with the specified initial value 624 */ 625 public static <T extends IBase> T newElement(FhirContext theFhirContext, String theElementType, Object theConstructorParam) { 626 BaseRuntimeElementDefinition def = theFhirContext.getElementDefinition(theElementType); 627 Validate.notNull(def); 628 return (T) def.newInstance(theConstructorParam); 629 } 630 631 /** 632 * Creates a new resource definition. 633 * 634 * @param theFhirContext Context holding resource definition 635 * @param theResourceName Name of the resource in the context 636 * @param <T> Type of the resource 637 * @return Returns a new instance of the resource 638 */ 639 public static <T extends IBase> T newResource(FhirContext theFhirContext, String theResourceName) { 640 RuntimeResourceDefinition def = theFhirContext.getResourceDefinition(theResourceName); 641 return (T) def.newInstance(); 642 } 643 644 /** 645 * Creates a new resource definition. 646 * 647 * @param theFhirContext Context holding resource definition 648 * @param theResourceName Name of the resource in the context 649 * @param theConstructorParam Initialization parameter for the new instance 650 * @param <T> Type of the resource 651 * @return Returns a new instance of the resource 652 */ 653 public static <T extends IBase> T newResource(FhirContext theFhirContext, String theResourceName, Object theConstructorParam) { 654 RuntimeResourceDefinition def = theFhirContext.getResourceDefinition(theResourceName); 655 return (T) def.newInstance(theConstructorParam); 656 } 657 658 private static void clear(List<IBase> values) { 659 if (values == null) { 660 return; 661 } 662 663 try { 664 values.clear(); 665 } catch (Throwable t) { 666 ourLog.debug("Unable to clear values " + String.valueOf(values), t); 667 } 668 } 669 670}