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 org.apache.commons.lang3.Validate; 030import org.apache.commons.lang3.tuple.Triple; 031import org.hl7.fhir.instance.model.api.IBase; 032import org.hl7.fhir.instance.model.api.IBaseResource; 033import org.hl7.fhir.instance.model.api.IPrimitiveType; 034import org.slf4j.Logger; 035 036import java.lang.reflect.Method; 037import java.util.Arrays; 038import java.util.Collection; 039import java.util.Collections; 040import java.util.List; 041import java.util.function.Predicate; 042import java.util.stream.Collectors; 043import java.util.stream.Stream; 044 045import static org.slf4j.LoggerFactory.getLogger; 046 047public final class TerserUtil { 048 049 public static final String FIELD_NAME_IDENTIFIER = "identifier"; 050 /** 051 * Exclude for id, identifier and meta fields of a resource. 052 */ 053 public static final Collection<String> IDS_AND_META_EXCLUDES = 054 Collections.unmodifiableSet(Stream.of("id", "identifier", "meta").collect(Collectors.toSet())); 055 /** 056 * Exclusion predicate for id, identifier, meta fields. 057 */ 058 public static final Predicate<String> EXCLUDE_IDS_AND_META = new Predicate<String>() { 059 @Override 060 public boolean test(String s) { 061 return !IDS_AND_META_EXCLUDES.contains(s); 062 } 063 }; 064 /** 065 * Exclusion predicate for id/identifier, meta and fields with empty values. This ensures that source / target resources, 066 * empty source fields will not results in erasure of target fields. 067 */ 068 public static final Predicate<Triple<BaseRuntimeChildDefinition, IBase, IBase>> EXCLUDE_IDS_META_AND_EMPTY = new Predicate<Triple<BaseRuntimeChildDefinition, IBase, IBase>>() { 069 @Override 070 public boolean test(Triple<BaseRuntimeChildDefinition, IBase, IBase> theTriple) { 071 if (!EXCLUDE_IDS_AND_META.test(theTriple.getLeft().getElementName())) { 072 return false; 073 } 074 BaseRuntimeChildDefinition childDefinition = theTriple.getLeft(); 075 boolean isSourceFieldEmpty = childDefinition.getAccessor().getValues(theTriple.getMiddle()).isEmpty(); 076 return !isSourceFieldEmpty; 077 } 078 }; 079 /** 080 * Exclusion predicate for keeping all fields. 081 */ 082 public static final Predicate<String> INCLUDE_ALL = new Predicate<String>() { 083 @Override 084 public boolean test(String s) { 085 return true; 086 } 087 }; 088 private static final Logger ourLog = getLogger(TerserUtil.class); 089 private static final String EQUALS_DEEP = "equalsDeep"; 090 091 private TerserUtil() { 092 } 093 094 /** 095 * Given an Child Definition of `identifier`, a R4/DSTU3 EID Identifier, and a new resource, clone the EID into that resources' identifier list. 096 */ 097 public static void cloneEidIntoResource(FhirContext theFhirContext, BaseRuntimeChildDefinition theIdentifierDefinition, IBase theEid, IBase theResourceToCloneEidInto) { 098 // FHIR choice types - fields within fhir where we have a choice of ids 099 BaseRuntimeElementCompositeDefinition<?> childIdentifier = (BaseRuntimeElementCompositeDefinition<?>) theIdentifierDefinition.getChildByName(FIELD_NAME_IDENTIFIER); 100 IBase resourceNewIdentifier = childIdentifier.newInstance(); 101 102 FhirTerser terser = theFhirContext.newTerser(); 103 terser.cloneInto(theEid, resourceNewIdentifier, true); 104 theIdentifierDefinition.getMutator().addValue(theResourceToCloneEidInto, resourceNewIdentifier); 105 } 106 107 /** 108 * Checks if the specified fields has any values 109 * 110 * @param theFhirContext Context holding resource definition 111 * @param theResource Resource to check if the specified field is set 112 * @param theFieldName name of the field to check 113 * @return Returns true if field exists and has any values set, and false otherwise 114 */ 115 public static boolean hasValues(FhirContext theFhirContext, IBaseResource theResource, String theFieldName) { 116 RuntimeResourceDefinition resourceDefinition = theFhirContext.getResourceDefinition(theResource); 117 BaseRuntimeChildDefinition resourceIdentifier = resourceDefinition.getChildByName(theFieldName); 118 if (resourceIdentifier == null) { 119 return false; 120 } 121 return !(resourceIdentifier.getAccessor().getValues(theResource).isEmpty()); 122 } 123 124 /** 125 * Gets all values of the specified field. 126 * 127 * @param theFhirContext Context holding resource definition 128 * @param theResource Resource to check if the specified field is set 129 * @param theFieldName name of the field to check 130 * @return Returns all values for the specified field or null if field with the provided name doesn't exist 131 */ 132 public static List<IBase> getValues(FhirContext theFhirContext, IBaseResource theResource, String theFieldName) { 133 RuntimeResourceDefinition resourceDefinition = theFhirContext.getResourceDefinition(theResource); 134 BaseRuntimeChildDefinition resourceIdentifier = resourceDefinition.getChildByName(theFieldName); 135 if (resourceIdentifier == null) { 136 ourLog.info("There is no field named {} in Resource {}", theFieldName, resourceDefinition.getName()); 137 return null; 138 } 139 return resourceIdentifier.getAccessor().getValues(theResource); 140 } 141 142 /** 143 * Gets the first available value for the specified field. 144 * 145 * @param theFhirContext Context holding resource definition 146 * @param theResource Resource to check if the specified field is set 147 * @param theFieldName name of the field to check 148 * @return Returns the first value for the specified field or null if field with the provided name doesn't exist or 149 * has no values 150 */ 151 public static IBase getValueFirstRep(FhirContext theFhirContext, IBaseResource theResource, String theFieldName) { 152 List<IBase> values = getValues(theFhirContext, theResource, theFieldName); 153 if (values == null || values.isEmpty()) { 154 return null; 155 } 156 return values.get(0); 157 } 158 159 /** 160 * Clones specified composite field (collection). Composite field values must conform to the collections 161 * contract. 162 * 163 * @param theFrom Resource to clone the specified field from 164 * @param theTo Resource to clone the specified field to 165 * @param theField Field name to be copied 166 */ 167 public static void cloneCompositeField(FhirContext theFhirContext, IBaseResource theFrom, IBaseResource theTo, String theField) { 168 FhirTerser terser = theFhirContext.newTerser(); 169 170 RuntimeResourceDefinition definition = theFhirContext.getResourceDefinition(theFrom); 171 BaseRuntimeChildDefinition childDefinition = definition.getChildByName(theField); 172 Validate.notNull(childDefinition); 173 174 List<IBase> theFromFieldValues = childDefinition.getAccessor().getValues(theFrom); 175 List<IBase> theToFieldValues = childDefinition.getAccessor().getValues(theTo); 176 177 for (IBase theFromFieldValue : theFromFieldValues) { 178 if (containsPrimitiveValue(theFromFieldValue, theToFieldValues)) { 179 continue; 180 } 181 182 IBase newFieldValue = newElement(childDefinition, theFromFieldValue, null); 183 terser.cloneInto(theFromFieldValue, newFieldValue, true); 184 185 try { 186 theToFieldValues.add(newFieldValue); 187 } catch (Exception e) { 188 childDefinition.getMutator().setValue(theTo, newFieldValue); 189 } 190 } 191 } 192 193 private static boolean containsPrimitiveValue(IBase theItem, List<IBase> theItems) { 194 PrimitiveTypeEqualsPredicate predicate = new PrimitiveTypeEqualsPredicate(); 195 return theItems.stream().anyMatch(i -> { 196 return predicate.test(i, theItem); 197 }); 198 } 199 200 private static Method getMethod(IBase theBase, String theMethodName) { 201 Method method = null; 202 for (Method m : theBase.getClass().getDeclaredMethods()) { 203 if (m.getName().equals(theMethodName)) { 204 method = m; 205 break; 206 } 207 } 208 return method; 209 } 210 211 /** 212 * Checks if two items are equal via {@link #EQUALS_DEEP} method 213 * 214 * @param theItem1 First item to compare 215 * @param theItem2 Second item to compare 216 * @return Returns true if they are equal and false otherwise 217 */ 218 public static boolean equals(IBase theItem1, IBase theItem2) { 219 if (theItem1 == null) { 220 return theItem2 == null; 221 } 222 223 final Method method = getMethod(theItem1, EQUALS_DEEP); 224 Validate.notNull(method); 225 return equals(theItem1, theItem2, method); 226 } 227 228 private static boolean equals(IBase theItem1, IBase theItem2, Method theMethod) { 229 if (theMethod != null) { 230 try { 231 return (Boolean) theMethod.invoke(theItem1, theItem2); 232 } catch (Exception e) { 233 throw new RuntimeException(String.format("Unable to compare equality via %s", EQUALS_DEEP), e); 234 } 235 } 236 return theItem1.equals(theItem2); 237 } 238 239 private static boolean contains(IBase theItem, List<IBase> theItems) { 240 final Method method = getMethod(theItem, EQUALS_DEEP); 241 return theItems.stream().anyMatch(i -> equals(i, theItem, method)); 242 } 243 244 /** 245 * Merges all fields on the provided instance. <code>theTo</code> will contain a union of all values from <code>theFrom</code> 246 * instance and <code>theTo</code> instance. 247 * 248 * @param theFhirContext Context holding resource definition 249 * @param theFrom The resource to merge the fields from 250 * @param theTo The resource to merge the fields into 251 */ 252 public static void mergeAllFields(FhirContext theFhirContext, IBaseResource theFrom, IBaseResource theTo) { 253 mergeFields(theFhirContext, theFrom, theTo, INCLUDE_ALL); 254 } 255 256 /** 257 * Replaces all fields that have matching field names by the given inclusion strategy. <code>theTo</code> will contain a copy of the 258 * values from <code>theFrom</code> instance. 259 * 260 * @param theFhirContext Context holding resource definition 261 * @param theFrom The resource to merge the fields from 262 * @param theTo The resource to merge the fields into 263 * @param theFieldNameInclusion Inclusion strategy that checks if a given field should be replaced 264 */ 265 public static void replaceFields(FhirContext theFhirContext, IBaseResource theFrom, IBaseResource theTo, Predicate<String> theFieldNameInclusion) { 266 Predicate<Triple<BaseRuntimeChildDefinition, IBase, IBase>> predicate 267 = (t) -> theFieldNameInclusion.test(t.getLeft().getElementName()); 268 replaceFieldsByPredicate(theFhirContext, theFrom, theTo, predicate); 269 } 270 271 /** 272 * Replaces fields on theTo resource that test positive by the given predicate. <code>theTo</code> will contain a copy of the 273 * values from <code>theFrom</code> for which predicate tests positive. Please note that composite fields will be replaced fully. 274 * 275 * @param theFhirContext Context holding resource definition 276 * @param theFrom The resource to merge the fields from 277 * @param theTo The resource to merge the fields into 278 * @param thePredicate Predicate that checks if a given field should be replaced 279 */ 280 public static void replaceFieldsByPredicate(FhirContext theFhirContext, IBaseResource theFrom, IBaseResource theTo, Predicate<Triple<BaseRuntimeChildDefinition, IBase, IBase>> thePredicate) { 281 RuntimeResourceDefinition definition = theFhirContext.getResourceDefinition(theFrom); 282 FhirTerser terser = theFhirContext.newTerser(); 283 for (BaseRuntimeChildDefinition childDefinition : definition.getChildrenAndExtension()) { 284 if (thePredicate.test(Triple.of(childDefinition, theFrom, theTo))) { 285 replaceField(terser, theFrom, theTo, childDefinition); 286 } 287 } 288 } 289 290 /** 291 * Checks if the field exists on the resource 292 * 293 * @param theFhirContext Context holding resource definition 294 * @param theFieldName Name of the field to check 295 * @param theInstance Resource instance to check 296 * @return Returns true if resource definition has a child with the specified name and false otherwise 297 */ 298 public static boolean fieldExists(FhirContext theFhirContext, String theFieldName, IBaseResource theInstance) { 299 RuntimeResourceDefinition definition = theFhirContext.getResourceDefinition(theInstance); 300 return definition.getChildByName(theFieldName) != null; 301 } 302 303 /** 304 * Replaces the specified fields on <code>theTo</code> resource with the value from <code>theFrom</code> resource. 305 * 306 * @param theFhirContext Context holding resource definition 307 * @param theFrom The resource to replace the field from 308 * @param theTo The resource to replace the field on 309 */ 310 public static void replaceField(FhirContext theFhirContext, String theFieldName, IBaseResource theFrom, IBaseResource theTo) { 311 RuntimeResourceDefinition definition = theFhirContext.getResourceDefinition(theFrom); 312 Validate.notNull(definition); 313 replaceField(theFhirContext.newTerser(), theFrom, theTo, theFhirContext.getResourceDefinition(theFrom).getChildByName(theFieldName)); 314 } 315 316 /** 317 * Clears the specified field on the resource provided 318 * 319 * @param theFhirContext Context holding resource definition 320 * @param theFieldName 321 * @param theResource 322 */ 323 public static void clearField(FhirContext theFhirContext, String theFieldName, IBaseResource theResource) { 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, String, IBaseResource)} 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, String, IBaseResource)} 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}