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