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