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
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}