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(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 * @param theFhirContext Context holding resource definition 320 * @param theResource 321 * @param theFieldName 322 */ 323 public static void clearField(FhirContext theFhirContext, IBaseResource theResource, String theFieldName) { 324 BaseRuntimeChildDefinition childDefinition = getBaseRuntimeChildDefinition(theFhirContext, theFieldName, theResource); 325 clear(childDefinition.getAccessor().getValues(theResource)); 326 } 327 328 /** 329 * Clears the specified field on the element provided 330 * 331 * @param theFhirContext Context holding resource definition 332 * @param theFieldName Name of the field to clear values for 333 * @param theBase The element definition to clear values on 334 */ 335 public static void clearField(FhirContext theFhirContext, String theFieldName, IBase theBase) { 336 BaseRuntimeElementDefinition definition = theFhirContext.getElementDefinition(theBase.getClass()); 337 BaseRuntimeChildDefinition childDefinition = definition.getChildByName(theFieldName); 338 Validate.notNull(childDefinition); 339 clear(childDefinition.getAccessor().getValues(theBase)); 340 } 341 342 /** 343 * Sets the provided field with the given values. This method will add to the collection of existing field values 344 * in case of multiple cardinality. Use {@link #clearField(FhirContext, IBaseResource, String)} 345 * to remove values before setting 346 * 347 * @param theFhirContext Context holding resource definition 348 * @param theFieldName Child field name of the resource to set 349 * @param theResource The resource to set the values on 350 * @param theValues The values to set on the resource child field name 351 */ 352 public static void setField(FhirContext theFhirContext, String theFieldName, IBaseResource theResource, IBase... theValues) { 353 setField(theFhirContext, theFhirContext.newTerser(), theFieldName, theResource, theValues); 354 } 355 356 /** 357 * Sets the provided field with the given values. This method will add to the collection of existing field values 358 * in case of multiple cardinality. Use {@link #clearField(FhirContext, IBaseResource, String)} 359 * to remove values before setting 360 * 361 * @param theFhirContext Context holding resource definition 362 * @param theTerser Terser to be used when cloning field values 363 * @param theFieldName Child field name of the resource to set 364 * @param theResource The resource to set the values on 365 * @param theValues The values to set on the resource child field name 366 */ 367 public static void setField(FhirContext theFhirContext, FhirTerser theTerser, String theFieldName, IBaseResource theResource, IBase... theValues) { 368 BaseRuntimeChildDefinition childDefinition = getBaseRuntimeChildDefinition(theFhirContext, theFieldName, theResource); 369 List<IBase> theFromFieldValues = childDefinition.getAccessor().getValues(theResource); 370 if (theFromFieldValues.isEmpty()) { 371 for (IBase value : theValues) { 372 try { 373 childDefinition.getMutator().addValue(theResource, value); 374 } catch (UnsupportedOperationException e) { 375 ourLog.warn("Resource {} does not support multiple values, but an attempt to set {} was made. Setting the first item only", theResource, theValues); 376 childDefinition.getMutator().setValue(theResource, value); 377 break; 378 } 379 } 380 return; 381 } 382 List<IBase> theToFieldValues = Arrays.asList(theValues); 383 mergeFields(theTerser, theResource, childDefinition, theFromFieldValues, theToFieldValues); 384 } 385 386 /** 387 * Sets the specified value at the FHIR path provided. 388 * 389 * @param theTerser The terser that should be used for cloning the field value. 390 * @param theFhirPath The FHIR path to set the field at 391 * @param theResource The resource on which the value should be set 392 * @param theValue The value to set 393 */ 394 public static void setFieldByFhirPath(FhirTerser theTerser, String theFhirPath, IBaseResource theResource, IBase theValue) { 395 List<IBase> theFromFieldValues = theTerser.getValues(theResource, theFhirPath, true, false); 396 for (IBase theFromFieldValue : theFromFieldValues) { 397 theTerser.cloneInto(theValue, theFromFieldValue, true); 398 } 399 } 400 401 /** 402 * Sets the specified value at the FHIR path provided. 403 * 404 * @param theFhirContext Context holding resource definition 405 * @param theFhirPath The FHIR path to set the field at 406 * @param theResource The resource on which the value should be set 407 * @param theValue The value to set 408 */ 409 public static void setFieldByFhirPath(FhirContext theFhirContext, String theFhirPath, IBaseResource theResource, IBase theValue) { 410 setFieldByFhirPath(theFhirContext.newTerser(), theFhirPath, theResource, theValue); 411 } 412 413 /** 414 * Returns field values ant the specified FHIR path from the resource. 415 * 416 * @param theFhirContext Context holding resource definition 417 * @param theFhirPath The FHIR path to get the field from 418 * @param theResource The resource from which the value should be retrieved 419 * @return Returns the list of field values at the given FHIR path 420 */ 421 public static List<IBase> getFieldByFhirPath(FhirContext theFhirContext, String theFhirPath, IBase theResource) { 422 return theFhirContext.newTerser().getValues(theResource, theFhirPath, false, false); 423 } 424 425 /** 426 * Returns the first available field value at the specified FHIR path from the resource. 427 * 428 * @param theFhirContext Context holding resource definition 429 * @param theFhirPath The FHIR path to get the field from 430 * @param theResource The resource from which the value should be retrieved 431 * @return Returns the first available value or null if no values can be retrieved 432 */ 433 public static IBase getFirstFieldByFhirPath(FhirContext theFhirContext, String theFhirPath, IBase theResource) { 434 List<IBase> values = getFieldByFhirPath(theFhirContext, theFhirPath, theResource); 435 if (values == null || values.isEmpty()) { 436 return null; 437 } 438 return values.get(0); 439 } 440 441 private static void replaceField(FhirTerser theTerser, IBaseResource theFrom, IBaseResource theTo, BaseRuntimeChildDefinition childDefinition) { 442 List<IBase> fromValues = childDefinition.getAccessor().getValues(theFrom); 443 List<IBase> toValues = childDefinition.getAccessor().getValues(theTo); 444 if (fromValues != toValues) { 445 clear(toValues); 446 447 mergeFields(theTerser, theTo, childDefinition, fromValues, toValues); 448 } 449 } 450 451 /** 452 * Merges values of all fields except for "identifier" and "meta" from <code>theFrom</code> resource to 453 * <code>theTo</code> resource. Fields values are compared via the equalsDeep method, or via object identity if this 454 * method is not available. 455 * 456 * @param theFhirContext Context holding resource definition 457 * @param theFrom Resource to merge the specified field from 458 * @param theTo Resource to merge the specified field into 459 */ 460 public static void mergeFieldsExceptIdAndMeta(FhirContext theFhirContext, IBaseResource theFrom, IBaseResource theTo) { 461 mergeFields(theFhirContext, theFrom, theTo, EXCLUDE_IDS_AND_META); 462 } 463 464 /** 465 * Merges values of all field from <code>theFrom</code> resource to <code>theTo</code> resource. Fields 466 * values are compared via the equalsDeep method, or via object identity if this method is not available. 467 * 468 * @param theFhirContext Context holding resource definition 469 * @param theFrom Resource to merge the specified field from 470 * @param theTo Resource to merge the specified field into 471 * @param inclusionStrategy Predicate to test which fields should be merged 472 */ 473 public static void mergeFields(FhirContext theFhirContext, IBaseResource theFrom, IBaseResource theTo, Predicate<String> inclusionStrategy) { 474 FhirTerser terser = theFhirContext.newTerser(); 475 476 RuntimeResourceDefinition definition = theFhirContext.getResourceDefinition(theFrom); 477 for (BaseRuntimeChildDefinition childDefinition : definition.getChildrenAndExtension()) { 478 if (!inclusionStrategy.test(childDefinition.getElementName())) { 479 continue; 480 } 481 482 List<IBase> theFromFieldValues = childDefinition.getAccessor().getValues(theFrom); 483 List<IBase> theToFieldValues = childDefinition.getAccessor().getValues(theTo); 484 485 mergeFields(terser, theTo, childDefinition, theFromFieldValues, theToFieldValues); 486 } 487 } 488 489 /** 490 * Merges value of the specified field from <code>theFrom</code> resource to <code>theTo</code> resource. Fields 491 * values are compared via the equalsDeep method, or via object identity if this method is not available. 492 * 493 * @param theFhirContext Context holding resource definition 494 * @param theFieldName Name of the child filed to merge 495 * @param theFrom Resource to merge the specified field from 496 * @param theTo Resource to merge the specified field into 497 */ 498 public static void mergeField(FhirContext theFhirContext, String theFieldName, IBaseResource theFrom, IBaseResource theTo) { 499 mergeField(theFhirContext, theFhirContext.newTerser(), theFieldName, theFrom, theTo); 500 } 501 502 /** 503 * Merges value of the specified field from <code>theFrom</code> resource to <code>theTo</code> resource. Fields 504 * values are compared via the equalsDeep method, or via object identity if this method is not available. 505 * 506 * @param theFhirContext Context holding resource definition 507 * @param theTerser Terser to be used when cloning the field values 508 * @param theFieldName Name of the child filed to merge 509 * @param theFrom Resource to merge the specified field from 510 * @param theTo Resource to merge the specified field into 511 */ 512 public static void mergeField(FhirContext theFhirContext, FhirTerser theTerser, String theFieldName, IBaseResource theFrom, IBaseResource theTo) { 513 BaseRuntimeChildDefinition childDefinition = getBaseRuntimeChildDefinition(theFhirContext, theFieldName, theFrom); 514 515 List<IBase> theFromFieldValues = childDefinition.getAccessor().getValues(theFrom); 516 List<IBase> theToFieldValues = childDefinition.getAccessor().getValues(theTo); 517 518 mergeFields(theTerser, theTo, childDefinition, theFromFieldValues, theToFieldValues); 519 } 520 521 private static BaseRuntimeChildDefinition getBaseRuntimeChildDefinition(FhirContext theFhirContext, String theFieldName, IBaseResource theFrom) { 522 RuntimeResourceDefinition definition = theFhirContext.getResourceDefinition(theFrom); 523 BaseRuntimeChildDefinition childDefinition = definition.getChildByName(theFieldName); 524 Validate.notNull(childDefinition); 525 return childDefinition; 526 } 527 528 /** 529 * Creates a new element taking into consideration elements with choice that are not directly retrievable by element 530 * name 531 * 532 * @param theChildDefinition Child to create a new instance for 533 * @param theFromFieldValue The base parent field 534 * @param theConstructorParam Optional constructor param 535 * @return Returns the new element with the given value if configured 536 */ 537 private static IBase newElement(BaseRuntimeChildDefinition theChildDefinition, IBase theFromFieldValue, Object theConstructorParam) { 538 BaseRuntimeElementDefinition runtimeElementDefinition; 539 if (theChildDefinition instanceof RuntimeChildChoiceDefinition) { 540 runtimeElementDefinition = theChildDefinition.getChildElementDefinitionByDatatype(theFromFieldValue.getClass()); 541 } else { 542 runtimeElementDefinition = theChildDefinition.getChildByName(theChildDefinition.getElementName()); 543 } 544 return (theConstructorParam == null) ? runtimeElementDefinition.newInstance() : runtimeElementDefinition.newInstance(theConstructorParam); 545 } 546 547 private static void mergeFields(FhirTerser theTerser, IBaseResource theTo, BaseRuntimeChildDefinition childDefinition, List<IBase> theFromFieldValues, List<IBase> theToFieldValues) { 548 for (IBase theFromFieldValue : theFromFieldValues) { 549 if (contains(theFromFieldValue, theToFieldValues)) { 550 continue; 551 } 552 553 IBase newFieldValue = newElement(childDefinition, theFromFieldValue, null); 554 if (theFromFieldValue instanceof IPrimitiveType) { 555 try { 556 Method copyMethod = getMethod(theFromFieldValue, "copy"); 557 if (copyMethod != null) { 558 newFieldValue = (IBase) copyMethod.invoke(theFromFieldValue, new Object[]{}); 559 } 560 } catch (Throwable t) { 561 ((IPrimitiveType) newFieldValue).setValueAsString(((IPrimitiveType) theFromFieldValue).getValueAsString()); 562 } 563 } else { 564 theTerser.cloneInto(theFromFieldValue, newFieldValue, true); 565 } 566 567 try { 568 theToFieldValues.add(newFieldValue); 569 } catch (UnsupportedOperationException e) { 570 childDefinition.getMutator().setValue(theTo, newFieldValue); 571 theToFieldValues = childDefinition.getAccessor().getValues(theTo); 572 } 573 } 574 } 575 576 /** 577 * Clones the specified resource. 578 * 579 * @param theFhirContext Context holding resource definition 580 * @param theInstance The instance to be cloned 581 * @param <T> Base resource type 582 * @return Returns a cloned instance 583 */ 584 public static <T extends IBaseResource> T clone(FhirContext theFhirContext, T theInstance) { 585 RuntimeResourceDefinition definition = theFhirContext.getResourceDefinition(theInstance.getClass()); 586 T retVal = (T) definition.newInstance(); 587 588 FhirTerser terser = theFhirContext.newTerser(); 589 terser.cloneInto(theInstance, retVal, true); 590 return retVal; 591 } 592 593 /** 594 * Creates a new element instance 595 * 596 * @param theFhirContext Context holding resource definition 597 * @param theElementType Element type name 598 * @param <T> Base element type 599 * @return Returns a new instance of the element 600 */ 601 public static <T extends IBase> T newElement(FhirContext theFhirContext, String theElementType) { 602 BaseRuntimeElementDefinition def = theFhirContext.getElementDefinition(theElementType); 603 return (T) def.newInstance(); 604 } 605 606 /** 607 * Creates a new element instance 608 * 609 * @param theFhirContext Context holding resource definition 610 * @param theElementType Element type name 611 * @param theConstructorParam Initialization parameter for the element 612 * @param <T> Base element type 613 * @return Returns a new instance of the element with the specified initial value 614 */ 615 public static <T extends IBase> T newElement(FhirContext theFhirContext, String theElementType, Object theConstructorParam) { 616 BaseRuntimeElementDefinition def = theFhirContext.getElementDefinition(theElementType); 617 Validate.notNull(def); 618 return (T) def.newInstance(theConstructorParam); 619 } 620 621 /** 622 * Creates a new resource definition. 623 * 624 * @param theFhirContext Context holding resource definition 625 * @param theResourceName Name of the resource in the context 626 * @param <T> Type of the resource 627 * @return Returns a new instance of the resource 628 */ 629 public static <T extends IBase> T newResource(FhirContext theFhirContext, String theResourceName) { 630 RuntimeResourceDefinition def = theFhirContext.getResourceDefinition(theResourceName); 631 return (T) def.newInstance(); 632 } 633 634 /** 635 * Creates a new resource definition. 636 * 637 * @param theFhirContext Context holding resource definition 638 * @param theResourceName Name of the resource in the context 639 * @param theConstructorParam Initialization parameter for the new instance 640 * @param <T> Type of the resource 641 * @return Returns a new instance of the resource 642 */ 643 public static <T extends IBase> T newResource(FhirContext theFhirContext, String theResourceName, Object theConstructorParam) { 644 RuntimeResourceDefinition def = theFhirContext.getResourceDefinition(theResourceName); 645 return (T) def.newInstance(theConstructorParam); 646 } 647 648 private static void clear(List<IBase> values) { 649 if (values == null) { 650 return; 651 } 652 653 try { 654 values.clear(); 655 } catch (Throwable t) { 656 ourLog.debug("Unable to clear values " + String.valueOf(values), t); 657 } 658 } 659 660}