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