001/*
002 * #%L
003 * HAPI FHIR - Core Library
004 * %%
005 * Copyright (C) 2014 - 2025 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.util;
021
022import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
023import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
024import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
025import ca.uhn.fhir.context.FhirContext;
026import ca.uhn.fhir.context.RuntimeResourceDefinition;
027import ca.uhn.fhir.i18n.Msg;
028import ca.uhn.fhir.model.api.annotation.Description;
029import ca.uhn.fhir.model.primitive.StringDt;
030import com.google.common.collect.Multimap;
031import com.google.common.collect.MultimapBuilder;
032import jakarta.annotation.Nullable;
033import org.apache.commons.lang3.Validate;
034import org.hl7.fhir.instance.model.api.IBase;
035import org.hl7.fhir.instance.model.api.IBaseDatatype;
036import org.hl7.fhir.instance.model.api.IBaseParameters;
037import org.hl7.fhir.instance.model.api.IBaseReference;
038import org.hl7.fhir.instance.model.api.IBaseResource;
039import org.hl7.fhir.instance.model.api.IPrimitiveType;
040
041import java.lang.annotation.Annotation;
042import java.lang.reflect.AnnotatedElement;
043import java.math.BigDecimal;
044import java.util.ArrayList;
045import java.util.Arrays;
046import java.util.Collection;
047import java.util.Date;
048import java.util.List;
049import java.util.Objects;
050import java.util.Optional;
051import java.util.function.BiConsumer;
052import java.util.function.Function;
053
054import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
055import static org.apache.commons.lang3.StringUtils.defaultString;
056import static org.apache.commons.lang3.StringUtils.isBlank;
057
058/**
059 * Utilities for dealing with parameters resources in a version independent way
060 */
061public class ParametersUtil {
062
063        private ParametersUtil() {}
064
065        public static Optional<String> getNamedParameterValueAsString(
066                        FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
067                Function<IPrimitiveType<?>, String> mapper = t -> defaultIfBlank(t.getValueAsString(), null);
068                return extractNamedParameterValues(theCtx, theParameters, theParameterName, mapper).stream()
069                                .findFirst();
070        }
071
072        public static List<String> getNamedParameterValuesAsString(
073                        FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
074                Function<IPrimitiveType<?>, String> mapper = t -> defaultIfBlank(t.getValueAsString(), null);
075                return extractNamedParameterValues(theCtx, theParameters, theParameterName, mapper);
076        }
077
078        public static List<Integer> getNamedParameterValuesAsInteger(
079                        FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
080                Function<IPrimitiveType<?>, Integer> mapper = t -> (Integer) t.getValue();
081                return extractNamedParameterValues(theCtx, theParameters, theParameterName, mapper);
082        }
083
084        public static Optional<Integer> getNamedParameterValueAsInteger(
085                        FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
086                return getNamedParameterValuesAsInteger(theCtx, theParameters, theParameterName).stream()
087                                .findFirst();
088        }
089
090        /**
091         * Returns the resource within a parameter.
092         * @param theCtx thr FHIR context
093         * @param theParameters the parameters instance where to look for the resource
094         * @param theParameterName the parameter name
095         * @return the resource
096         */
097        public static Optional<IBaseResource> getNamedParameterResource(
098                        FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
099
100                List<IBase> namedParameters = getNamedParameters(theCtx, theParameters, theParameterName);
101                return collectChildrenByNameAndType(theCtx, namedParameters, "resource", IBaseResource.class).stream()
102                                .findFirst();
103        }
104
105        private static <T extends IBase> List<T> collectChildrenByNameAndType(
106                        FhirContext theCtx, List<IBase> theNamedParameters, String theChildName, Class<T> childrenType) {
107
108                List<T> collectedChildren = new ArrayList<>();
109
110                for (IBase nextParameter : theNamedParameters) {
111                        BaseRuntimeElementCompositeDefinition<?> nextParameterDef =
112                                        (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(nextParameter.getClass());
113                        BaseRuntimeChildDefinition resourceChild = nextParameterDef.getChildByName(theChildName);
114                        List<IBase> resourceValues = resourceChild.getAccessor().getValues(nextParameter);
115                        resourceValues.stream()
116                                        .filter(childrenType::isInstance)
117                                        .map(childrenType::cast)
118                                        .forEach(collectedChildren::add);
119                }
120
121                return collectedChildren;
122        }
123
124        /**
125         * Returns the list of parameter reference.
126         * @param theCtx thr FHIR context
127         * @param theParameters the parameters instance where to look for the reference
128         * @param theParameterName the parameter name
129         * @return the reference list
130         */
131        public static List<IBaseReference> getNamedParameterReferences(
132                        FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
133                List<IBase> namedParameters = getNamedParameters(theCtx, theParameters, theParameterName);
134                return collectChildrenByNameAndType(theCtx, namedParameters, "valueReference", IBaseReference.class);
135        }
136
137        public static Optional<IBase> getNamedParameter(
138                        FhirContext theCtx, IBaseResource theParameters, String theParameterName) {
139                return getNamedParameters(theCtx, theParameters, theParameterName).stream()
140                                .findFirst();
141        }
142
143        /**
144         * Returns a map containing all of the named parameters in a Parameters resource. The key will be the
145         * <code>Parameters.parameter.name</code> value (or an empty string <code>""</code> if no name is present)
146         * and the value will be the <code>Parameters.parameter</code> repetition.
147         *
148         * @param theCtx The FhirContext instance appropriate for the input
149         * @param theParameters The Parameters resource to examine
150         * @return A map containing named parameters
151         * @since 8.4.0
152         */
153        public static Multimap<String, IBase> getNamedParameters(FhirContext theCtx, IBaseResource theParameters) {
154                Multimap<String, IBase> retVal =
155                                MultimapBuilder.hashKeys().arrayListValues().build();
156                BiConsumer<String, IBase> consumer = (paramName, part) -> {
157                        paramName = defaultString(paramName);
158                        retVal.put(paramName, part);
159                };
160
161                getNamedParameters(theCtx, theParameters, consumer);
162
163                return retVal;
164        }
165
166        public static List<IBase> getNamedParameters(
167                        FhirContext theCtx, IBaseResource theParameters, String theParameterName) {
168                List<IBase> retVal = new ArrayList<>();
169                BiConsumer<String, IBase> consumer = (paramName, part) -> {
170                        if (theParameterName.equals(paramName)) {
171                                retVal.add(part);
172                        }
173                };
174
175                getNamedParameters(theCtx, theParameters, consumer);
176
177                return retVal;
178        }
179
180        private static void getNamedParameters(
181                        FhirContext theCtx, IBaseResource theParameters, BiConsumer<String, IBase> theConsumer) {
182                Validate.notNull(theParameters, "theParameters must not be null");
183
184                FhirTerser terser = theCtx.newTerser();
185                List<IBase> parameterReps = terser.getValues(theParameters, "parameter");
186
187                for (IBase parameter : parameterReps) {
188                        String name = terser.getSinglePrimitiveValueOrNull(parameter, "name");
189                        theConsumer.accept(name, parameter);
190                }
191        }
192
193        public static Optional<IBase> getParameterPart(FhirContext theCtx, IBase theParameter, String theParameterName) {
194                BaseRuntimeElementCompositeDefinition<?> nextParameterDef =
195                                (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(theParameter.getClass());
196                BaseRuntimeChildDefinition valueChild = nextParameterDef.getChildByName("part");
197                List<IBase> parts = valueChild.getAccessor().getValues(theParameter);
198
199                for (IBase nextPart : parts) {
200                        Optional<IPrimitiveType> name = theCtx.newTerser().getSingleValue(nextPart, "name", IPrimitiveType.class);
201                        if (name.isPresent() && theParameterName.equals(name.get().getValueAsString())) {
202                                return Optional.of(nextPart);
203                        }
204                }
205
206                return Optional.empty();
207        }
208
209        public static Optional<IBase> getParameterPartValue(
210                        FhirContext theCtx, IBase theParameter, String theParameterName) {
211                Optional<IBase> part = getParameterPart(theCtx, theParameter, theParameterName);
212                if (part.isPresent()) {
213                        return theCtx.newTerser().getSingleValue(part.get(), "value[x]", IBase.class);
214                } else {
215                        return Optional.empty();
216                }
217        }
218
219        public static String getParameterPartValueAsString(
220                        FhirContext theCtx, IBase theParameter, String theParameterName) {
221                return getParameterPartValue(theCtx, theParameter, theParameterName)
222                                .map(t -> (IPrimitiveType<?>) t)
223                                .map(IPrimitiveType::getValueAsString)
224                                .orElse(null);
225        }
226
227        public static Optional<Integer> getParameterPartValueAsInteger(
228                        FhirContext theCtx, IBase theParameter, String theParameterName) {
229                return getParameterPartValue(theCtx, theParameter, theParameterName)
230                                .filter(t -> IPrimitiveType.class.isAssignableFrom(t.getClass()))
231                                .map(t -> (IPrimitiveType<?>) t)
232                                .map(IPrimitiveType::getValue)
233                                .filter(t -> Integer.class.isAssignableFrom(t.getClass()))
234                                .map(t -> (Integer) t);
235        }
236
237        /**
238         * @since 8.6.0
239         */
240        public static Optional<Boolean> getParameterPartValueAsBoolean(
241                        FhirContext theCtx, IBase theParameter, String theParameterName) {
242                return getParameterPartValue(theCtx, theParameter, theParameterName)
243                                .filter(t -> IPrimitiveType.class.isAssignableFrom(t.getClass()))
244                                .map(t -> (IPrimitiveType<?>) t)
245                                .map(IPrimitiveType::getValue)
246                                .filter(t -> Boolean.class.isAssignableFrom(t.getClass()))
247                                .map(t -> (Boolean) t);
248        }
249
250        private static <T> List<T> extractNamedParameterValues(
251                        FhirContext theCtx,
252                        IBaseParameters theParameters,
253                        String theParameterName,
254                        Function<IPrimitiveType<?>, T> theMapper) {
255                List<T> retVal = new ArrayList<>();
256
257                List<IBase> namedParameters = getNamedParameters(theCtx, theParameters, theParameterName);
258                for (IBase nextParameter : namedParameters) {
259                        BaseRuntimeElementCompositeDefinition<?> nextParameterDef =
260                                        (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(nextParameter.getClass());
261                        BaseRuntimeChildDefinition valueChild = nextParameterDef.getChildByName("value[x]");
262                        List<IBase> valueValues = valueChild.getAccessor().getValues(nextParameter);
263                        valueValues.stream()
264                                        .filter(t -> t instanceof IPrimitiveType<?>)
265                                        .map(t -> ((IPrimitiveType<?>) t))
266                                        .map(theMapper)
267                                        .filter(Objects::nonNull)
268                                        .forEach(retVal::add);
269                }
270                return retVal;
271        }
272
273        private static void addClientParameter(
274                        FhirContext theContext,
275                        Object theValue,
276                        IBaseResource theTargetResource,
277                        BaseRuntimeChildDefinition paramChild,
278                        BaseRuntimeElementCompositeDefinition<?> paramChildElem,
279                        String theName) {
280                Validate.notNull(theValue, "theValue must not be null");
281
282                if (theValue instanceof IBaseResource) {
283                        IBase parameter =
284                                        createParameterRepetition(theContext, theTargetResource, paramChild, paramChildElem, theName);
285                        paramChildElem.getChildByName("resource").getMutator().addValue(parameter, (IBaseResource) theValue);
286                } else if (theValue instanceof IBaseDatatype) {
287                        IBase parameter =
288                                        createParameterRepetition(theContext, theTargetResource, paramChild, paramChildElem, theName);
289                        paramChildElem.getChildByName("value[x]").getMutator().addValue(parameter, (IBaseDatatype) theValue);
290                } else if (theValue instanceof Collection<?> collection) {
291                        for (Object next : collection) {
292                                addClientParameter(theContext, next, theTargetResource, paramChild, paramChildElem, theName);
293                        }
294                } else {
295                        throw new IllegalArgumentException(Msg.code(1806) + "Don't know how to handle value of type "
296                                        + theValue.getClass() + " for parameter " + theName);
297                }
298        }
299
300        /**
301         * Add a parameter value to a Parameters resource
302         *
303         * @param theContext    The FhirContext
304         * @param theParameters The Parameters resource
305         * @param theName       The parameter name
306         * @param theValue      The parameter value (can be a {@link IBaseResource resource} or a {@link IBaseDatatype datatype})
307         */
308        public static void addParameterToParameters(
309                        FhirContext theContext, IBaseParameters theParameters, String theName, Object theValue) {
310                RuntimeResourceDefinition def = theContext.getResourceDefinition(theParameters);
311                BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter");
312                BaseRuntimeElementCompositeDefinition<?> paramChildElem =
313                                (BaseRuntimeElementCompositeDefinition<?>) paramChild.getChildByName("parameter");
314
315                addClientParameter(theContext, theValue, theParameters, paramChild, paramChildElem, theName);
316        }
317
318        /**
319         * Add a parameter value to a Parameters resource
320         *
321         * @param theContext           The FhirContext
322         * @param theParameters        The Parameters resource
323         * @param theName              The parameter name
324         * @param thePrimitiveDatatype The datatype, e.g. "string", or "uri"
325         * @param theValue             The value
326         */
327        public static void addParameterToParameters(
328                        FhirContext theContext,
329                        IBaseParameters theParameters,
330                        String theName,
331                        String thePrimitiveDatatype,
332                        String theValue) {
333                Validate.notBlank(thePrimitiveDatatype, "thePrimitiveDatatype must not be null or empty");
334
335                BaseRuntimeElementDefinition<?> datatypeDef = theContext.getElementDefinition(thePrimitiveDatatype);
336                assert datatypeDef != null;
337                IPrimitiveType<?> value = (IPrimitiveType<?>) datatypeDef.newInstance();
338                value.setValueAsString(theValue);
339
340                addParameterToParameters(theContext, theParameters, theName, value);
341        }
342
343        private static IBase createParameterRepetition(
344                        FhirContext theContext,
345                        IBase theTargetResource,
346                        BaseRuntimeChildDefinition paramChild,
347                        BaseRuntimeElementCompositeDefinition<?> paramChildElem,
348                        String theName) {
349                IBase parameter = paramChildElem.newInstance();
350                paramChild.getMutator().addValue(theTargetResource, parameter);
351                IPrimitiveType<?> value;
352                value = createString(theContext, theName);
353                paramChildElem.getChildByName("name").getMutator().addValue(parameter, value);
354                return parameter;
355        }
356
357        public static IPrimitiveType<?> createString(FhirContext theContext, String theValue) {
358                IPrimitiveType<?> value;
359                if (theContext.getVersion().getVersion().isRi()) {
360                        value = (IPrimitiveType<?>) Objects.requireNonNull(theContext.getElementDefinition("string"))
361                                        .newInstance(theValue);
362                } else {
363                        value = new StringDt(theValue);
364                }
365                return value;
366        }
367
368        public static IPrimitiveType<?> createUri(FhirContext theContext, String theValue) {
369                return (IPrimitiveType<?>)
370                                Objects.requireNonNull(theContext.getElementDefinition("uri")).newInstance(theValue);
371        }
372
373        public static IPrimitiveType<?> createCode(FhirContext theContext, String theValue) {
374                return (IPrimitiveType<?>)
375                                Objects.requireNonNull(theContext.getElementDefinition("code")).newInstance(theValue);
376        }
377
378        public static IPrimitiveType<?> createInstant(FhirContext theContext, Date theValue) {
379                return (IPrimitiveType<?>) Objects.requireNonNull(theContext.getElementDefinition("instant"))
380                                .newInstance(theValue);
381        }
382
383        public static IBaseParameters newInstance(FhirContext theContext) {
384                Validate.notNull(theContext, "theContext must not be null");
385                return (IBaseParameters) theContext.getResourceDefinition("Parameters").newInstance();
386        }
387
388        public static void addParameterToParametersBoolean(
389                        FhirContext theCtx, IBaseParameters theParameters, String theName, boolean theValue) {
390                addParameterToParameters(theCtx, theParameters, theName, theCtx.newPrimitiveBoolean(theValue));
391        }
392
393        @SuppressWarnings("unchecked")
394        public static void addParameterToParametersCode(
395                        FhirContext theCtx, IBaseParameters theParameters, String theName, String theValue) {
396                IPrimitiveType<String> value = (IPrimitiveType<String>)
397                                Objects.requireNonNull(theCtx.getElementDefinition("code")).newInstance();
398                value.setValue(theValue);
399                addParameterToParameters(theCtx, theParameters, theName, value);
400        }
401
402        @SuppressWarnings("unchecked")
403        public static void addParameterToParametersInteger(
404                        FhirContext theCtx, IBaseParameters theParameters, String theName, int theValue) {
405                IPrimitiveType<Integer> count = (IPrimitiveType<Integer>)
406                                Objects.requireNonNull(theCtx.getElementDefinition("integer")).newInstance();
407                count.setValue(theValue);
408                addParameterToParameters(theCtx, theParameters, theName, count);
409        }
410
411        public static void addParameterToParametersLong(
412                        FhirContext theCtx, IBaseParameters theParameters, String theName, long theValue) {
413                addParameterToParametersDecimal(theCtx, theParameters, theName, BigDecimal.valueOf(theValue));
414        }
415
416        public static void addParameterToParametersDecimal(
417                        FhirContext theCtx, IBaseParameters theParameters, String theName, BigDecimal theValue) {
418                IPrimitiveType<BigDecimal> count = (IPrimitiveType<BigDecimal>)
419                                Objects.requireNonNull(theCtx.getElementDefinition("decimal")).newInstance();
420                count.setValue(theValue);
421                addParameterToParameters(theCtx, theParameters, theName, count);
422        }
423
424        public static void addParameterToParametersReference(
425                        FhirContext theCtx, IBaseParameters theParameters, String theName, String theReference) {
426                IBaseReference target = (IBaseReference)
427                                Objects.requireNonNull(theCtx.getElementDefinition("reference")).newInstance();
428                target.setReference(theReference);
429                addParameterToParameters(theCtx, theParameters, theName, target);
430        }
431
432        @SuppressWarnings("unchecked")
433        public static void addParameterToParametersString(
434                        FhirContext theCtx, IBaseParameters theParameters, String theName, String theValue) {
435                IPrimitiveType<String> value = (IPrimitiveType<String>)
436                                Objects.requireNonNull(theCtx.getElementDefinition("string")).newInstance();
437                value.setValue(theValue);
438                addParameterToParameters(theCtx, theParameters, theName, value);
439        }
440
441        @SuppressWarnings("unchecked")
442        public static void addParameterToParametersUri(
443                        FhirContext theCtx, IBaseParameters theParameters, String theName, String theValue) {
444                IPrimitiveType<String> value = (IPrimitiveType<String>)
445                                Objects.requireNonNull(theCtx.getElementDefinition("uri")).newInstance();
446                value.setValue(theValue);
447                addParameterToParameters(theCtx, theParameters, theName, value);
448        }
449
450        /**
451         * Add a parameter with no value (typically because we'll be adding sub-parameters)
452         */
453        public static IBase addParameterToParameters(
454                        FhirContext theContext, IBaseParameters theParameters, String theName) {
455                RuntimeResourceDefinition def = theContext.getResourceDefinition(theParameters);
456                BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter");
457                BaseRuntimeElementCompositeDefinition<?> paramChildElem =
458                                (BaseRuntimeElementCompositeDefinition<?>) paramChild.getChildByName("parameter");
459
460                return createParameterRepetition(theContext, theParameters, paramChild, paramChildElem, theName);
461        }
462
463        public static void addPartCode(FhirContext theContext, IBase theParameter, String theName, String theCode) {
464                IPrimitiveType<String> value = (IPrimitiveType<String>)
465                                Objects.requireNonNull(theContext.getElementDefinition("code")).newInstance();
466                value.setValue(theCode);
467
468                addPart(theContext, theParameter, theName, value);
469        }
470
471        public static void addPartInteger(FhirContext theContext, IBase theParameter, String theName, Integer theInteger) {
472                IPrimitiveType<Integer> value =
473                                (IPrimitiveType<Integer>) Objects.requireNonNull(theContext.getElementDefinition("integer"))
474                                                .newInstance();
475                value.setValue(theInteger);
476
477                addPart(theContext, theParameter, theName, value);
478        }
479
480        public static void addPartString(FhirContext theContext, IBase theParameter, String theName, String theValue) {
481                IPrimitiveType<String> value =
482                                (IPrimitiveType<String>) Objects.requireNonNull(theContext.getElementDefinition("string"))
483                                                .newInstance();
484                value.setValue(theValue);
485
486                addPart(theContext, theParameter, theName, value);
487        }
488
489        public static void addPartUrl(FhirContext theContext, IBase theParameter, String theName, String theCode) {
490                IPrimitiveType<String> value = (IPrimitiveType<String>)
491                                Objects.requireNonNull(theContext.getElementDefinition("url")).newInstance();
492                value.setValue(theCode);
493
494                addPart(theContext, theParameter, theName, value);
495        }
496
497        /**
498         * @return Returns the added part
499         */
500        public static IBase addPartBoolean(FhirContext theContext, IBase theParameter, String theName, Boolean theValue) {
501                return addPart(theContext, theParameter, theName, theContext.newPrimitiveBoolean(theValue));
502        }
503
504        public static void addPartDecimal(FhirContext theContext, IBase theParameter, String theName, Double theValue) {
505                IPrimitiveType<BigDecimal> value = (IPrimitiveType<BigDecimal>)
506                                theContext.getElementDefinition("decimal").newInstance();
507                if (theValue == null) {
508                        value.setValue(null);
509                } else {
510                        BigDecimal decimalValue = BigDecimal.valueOf(theValue);
511                        if (decimalValue.scale() < 0) {
512                                decimalValue = decimalValue.setScale(0);
513                        }
514                        value.setValue(decimalValue);
515                }
516                addPart(theContext, theParameter, theName, value);
517        }
518
519        public static void addPartCoding(
520                        FhirContext theContext,
521                        IBase theParameter,
522                        String theName,
523                        String theSystem,
524                        String theCode,
525                        String theDisplay) {
526                IBase coding = Objects.requireNonNull(theContext.getElementDefinition("coding"))
527                                .newInstance();
528
529                BaseRuntimeElementCompositeDefinition<?> codingDef =
530                                (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(coding.getClass());
531                codingDef.getChildByName("system").getMutator().addValue(coding, createUri(theContext, theSystem));
532                codingDef.getChildByName("code").getMutator().addValue(coding, createCode(theContext, theCode));
533                codingDef.getChildByName("display").getMutator().addValue(coding, createString(theContext, theDisplay));
534
535                addPart(theContext, theParameter, theName, coding);
536        }
537
538        public static IBase addPart(FhirContext theContext, IBase theParameter, String theName, @Nullable IBase theValue) {
539                BaseRuntimeElementCompositeDefinition<?> def =
540                                (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theParameter.getClass());
541                BaseRuntimeChildDefinition partChild = def.getChildByName("part");
542
543                BaseRuntimeElementCompositeDefinition<?> partChildElem =
544                                (BaseRuntimeElementCompositeDefinition<?>) partChild.getChildByName("part");
545                IBase part = partChildElem.newInstance();
546                partChild.getMutator().addValue(theParameter, part);
547
548                IPrimitiveType<String> name = (IPrimitiveType<String>)
549                                theContext.getElementDefinition("string").newInstance();
550                name.setValue(theName);
551                partChildElem.getChildByName("name").getMutator().addValue(part, name);
552
553                if (theValue != null) {
554                        if (theValue instanceof IBaseResource) {
555                                partChildElem.getChildByName("resource").getMutator().addValue(part, theValue);
556                        } else {
557                                partChildElem.getChildByName("value[x]").getMutator().addValue(part, theValue);
558                        }
559                }
560                return part;
561        }
562
563        public static IBase createPart(FhirContext theContext, IBase thePart, String theName) {
564                BaseRuntimeElementCompositeDefinition<?> def =
565                                (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(thePart.getClass());
566                BaseRuntimeChildDefinition partChild = def.getChildByName("part");
567
568                BaseRuntimeElementCompositeDefinition<?> partChildElem =
569                                (BaseRuntimeElementCompositeDefinition<?>) partChild.getChildByName("part");
570
571                return createParameterRepetition(theContext, thePart, partChild, partChildElem, theName);
572        }
573
574        public static void addPartResource(
575                        FhirContext theContext, IBase theParameter, String theName, IBaseResource theValue) {
576                BaseRuntimeElementCompositeDefinition<?> def =
577                                (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theParameter.getClass());
578                BaseRuntimeChildDefinition partChild = def.getChildByName("part");
579
580                BaseRuntimeElementCompositeDefinition<?> partChildElem =
581                                (BaseRuntimeElementCompositeDefinition<?>) partChild.getChildByName("part");
582                IBase part = partChildElem.newInstance();
583                partChild.getMutator().addValue(theParameter, part);
584
585                IPrimitiveType<String> name = (IPrimitiveType<String>)
586                                theContext.getElementDefinition("string").newInstance();
587                name.setValue(theName);
588                partChildElem.getChildByName("name").getMutator().addValue(part, name);
589
590                partChildElem.getChildByName("resource").getMutator().addValue(part, theValue);
591        }
592
593        public static List<String> getNamedParameterPartAsString(
594                        FhirContext theCtx, IBaseParameters theParameters, String thePartName, String theParameterName) {
595                return extractNamedParameterPartsAsString(theCtx, theParameters, thePartName, theParameterName);
596        }
597
598        // TODO KHS need to consolidate duplicated functionality that came in from different branches
599        private static List<String> extractNamedParameterPartsAsString(
600                        FhirContext theCtx, IBaseParameters theParameters, String thePartName, String theParameterName) {
601                List<IBase> parameterReps = getParameterReps(theCtx, theParameters);
602
603                List<String> retVal = new ArrayList<>();
604
605                for (IBase nextParameter : parameterReps) {
606                        BaseRuntimeElementCompositeDefinition<?> nextParameterDef =
607                                        (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(nextParameter.getClass());
608                        Optional<? extends IPrimitiveType<?>> nameValue = getNameValue(nextParameter, nextParameterDef);
609                        if (nameValue.isEmpty() || !thePartName.equals(nameValue.get().getValueAsString())) {
610                                continue;
611                        }
612
613                        BaseRuntimeChildDefinition partChild = nextParameterDef.getChildByName("part");
614                        List<IBase> partValues = partChild.getAccessor().getValues(nextParameter);
615                        for (IBase partValue : partValues) {
616                                BaseRuntimeElementCompositeDefinition<?> partParameterDef =
617                                                (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(partValue.getClass());
618                                Optional<? extends IPrimitiveType<?>> partNameValue = getNameValue(partValue, partParameterDef);
619                                if (partNameValue.isEmpty()
620                                                || !theParameterName.equals(partNameValue.get().getValueAsString())) {
621                                        continue;
622                                }
623                                BaseRuntimeChildDefinition valueChild = partParameterDef.getChildByName("value[x]");
624                                List<IBase> valueValues = valueChild.getAccessor().getValues(partValue);
625                                valueValues.stream()
626                                                .filter(t -> t instanceof IPrimitiveType<?>)
627                                                .map(t -> ((IPrimitiveType<String>) t))
628                                                .map(t -> defaultIfBlank(t.getValueAsString(), null))
629                                                .filter(Objects::nonNull)
630                                                .forEach(retVal::add);
631                        }
632                }
633                return retVal;
634        }
635
636        private static List<IBase> getParameterReps(FhirContext theCtx, IBaseParameters theParameters) {
637                Validate.notNull(theParameters, "theParameters must not be null");
638                RuntimeResourceDefinition resDef = theCtx.getResourceDefinition(theParameters.getClass());
639                BaseRuntimeChildDefinition parameterChild = resDef.getChildByName("parameter");
640                return parameterChild.getAccessor().getValues(theParameters);
641        }
642
643        private static Optional<? extends IPrimitiveType<?>> getNameValue(
644                        IBase nextParameter, BaseRuntimeElementCompositeDefinition<?> theNextParameterDef) {
645                BaseRuntimeChildDefinition nameChild = theNextParameterDef.getChildByName("name");
646                List<IBase> nameValues = nameChild.getAccessor().getValues(nextParameter);
647                return nameValues.stream()
648                                .filter(t -> t instanceof IPrimitiveType<?>)
649                                .map(t -> ((IPrimitiveType<?>) t))
650                                .findFirst();
651        }
652
653        @Nullable
654        public static String extractDescription(AnnotatedElement theType) {
655                Description description = theType.getAnnotation(Description.class);
656                if (description != null) {
657                        return extractDescription(description);
658                } else {
659                        return null;
660                }
661        }
662
663        @Nullable
664        public static String extractDescription(Description desc) {
665                String description = desc.value();
666                if (isBlank(description)) {
667                        description = desc.formalDefinition();
668                }
669                if (isBlank(description)) {
670                        description = desc.shortDefinition();
671                }
672                return defaultIfBlank(description, null);
673        }
674
675        @Nullable
676        public static String extractShortDefinition(AnnotatedElement theType) {
677                Description description = theType.getAnnotation(Description.class);
678                if (description != null) {
679                        return defaultIfBlank(description.shortDefinition(), null);
680                } else {
681                        return null;
682                }
683        }
684
685        public static String extractDescription(Annotation[] theParameterAnnotations) {
686                for (Annotation next : theParameterAnnotations) {
687                        if (next instanceof Description) {
688                                return extractDescription((Description) next);
689                        }
690                }
691                return null;
692        }
693
694        public static List<String> extractExamples(Annotation[] theParameterAnnotations) {
695                ArrayList<String> retVal = null;
696                for (Annotation next : theParameterAnnotations) {
697                        if (next instanceof Description) {
698                                String[] examples = ((Description) next).example();
699                                if (examples.length > 0) {
700                                        if (retVal == null) {
701                                                retVal = new ArrayList<>();
702                                        }
703                                        retVal.addAll(Arrays.asList(examples));
704                                }
705                        }
706                }
707                return retVal;
708        }
709}