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