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