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