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