001/*
002 * #%L
003 * HAPI FHIR - Core Library
004 * %%
005 * Copyright (C) 2014 - 2023 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 org.apache.commons.lang3.Validate;
031import org.hl7.fhir.instance.model.api.IBase;
032import org.hl7.fhir.instance.model.api.IBaseDatatype;
033import org.hl7.fhir.instance.model.api.IBaseParameters;
034import org.hl7.fhir.instance.model.api.IBaseReference;
035import org.hl7.fhir.instance.model.api.IBaseResource;
036import org.hl7.fhir.instance.model.api.IPrimitiveType;
037
038import javax.annotation.Nullable;
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.Optional;
047import java.util.function.Function;
048import java.util.stream.Collectors;
049
050import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
051import static org.apache.commons.lang3.StringUtils.isBlank;
052
053/**
054 * Utilities for dealing with parameters resources in a version indepenedent way
055 */
056public class ParametersUtil {
057
058        public static Optional<String> getNamedParameterValueAsString(FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
059                Function<IPrimitiveType<?>, String> mapper = t -> defaultIfBlank(t.getValueAsString(), null);
060                return extractNamedParameters(theCtx, theParameters, theParameterName, mapper).stream().findFirst();
061        }
062
063        public static List<String> getNamedParameterValuesAsString(FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
064                Function<IPrimitiveType<?>, String> mapper = t -> defaultIfBlank(t.getValueAsString(), null);
065                return extractNamedParameters(theCtx, theParameters, theParameterName, mapper);
066        }
067
068        public static List<Integer> getNamedParameterValuesAsInteger(FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
069                Function<IPrimitiveType<?>, Integer> mapper = t -> (Integer) t.getValue();
070                return extractNamedParameters(theCtx, theParameters, theParameterName, mapper);
071        }
072
073        public static Optional<Integer> getNamedParameterValueAsInteger(FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
074                return getNamedParameterValuesAsInteger(theCtx, theParameters, theParameterName).stream().findFirst();
075        }
076
077        public static Optional<IBase> getNamedParameter(FhirContext theCtx, IBaseResource theParameters, String theParameterName) {
078                return getNamedParameters(theCtx, theParameters, theParameterName).stream().findFirst();
079        }
080
081        public static List<IBase> getNamedParameters(FhirContext theCtx, IBaseResource theParameters, String theParameterName) {
082                Validate.notNull(theParameters, "theParameters must not be null");
083                RuntimeResourceDefinition resDef = theCtx.getResourceDefinition(theParameters.getClass());
084                BaseRuntimeChildDefinition parameterChild = resDef.getChildByName("parameter");
085                List<IBase> parameterReps = parameterChild.getAccessor().getValues(theParameters);
086
087                return parameterReps
088                        .stream()
089                        .filter(param -> {
090                                BaseRuntimeElementCompositeDefinition<?> nextParameterDef = (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(param.getClass());
091                                BaseRuntimeChildDefinition nameChild = nextParameterDef.getChildByName("name");
092                                List<IBase> nameValues = nameChild.getAccessor().getValues(param);
093                                Optional<? extends IPrimitiveType<?>> nameValue = nameValues
094                                        .stream()
095                                        .filter(t -> t instanceof IPrimitiveType<?>)
096                                        .map(t -> ((IPrimitiveType<?>) t))
097                                        .findFirst();
098                                return nameValue.isPresent() && theParameterName.equals(nameValue.get().getValueAsString());
099                        })
100                        .collect(Collectors.toList());
101
102        }
103
104        public static Optional<IBase> getParameterPart(FhirContext theCtx, IBase theParameter, String theParameterName) {
105                BaseRuntimeElementCompositeDefinition<?> nextParameterDef = (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(theParameter.getClass());
106                BaseRuntimeChildDefinition valueChild = nextParameterDef.getChildByName("part");
107                List<IBase> parts = valueChild.getAccessor().getValues(theParameter);
108
109                for (IBase nextPart : parts) {
110                        Optional<IPrimitiveType> name = theCtx.newTerser().getSingleValue(nextPart, "name", IPrimitiveType.class);
111                        if (name.isPresent() && theParameterName.equals(name.get().getValueAsString())) {
112                                return Optional.of(nextPart);
113                        }
114                }
115
116                return Optional.empty();
117        }
118
119        public static Optional<IBase> getParameterPartValue(FhirContext theCtx, IBase theParameter, String theParameterName) {
120                Optional<IBase> part = getParameterPart(theCtx, theParameter, theParameterName);
121                if (part.isPresent()) {
122                        return theCtx.newTerser().getSingleValue(part.get(), "value[x]", IBase.class);
123                } else {
124                        return Optional.empty();
125                }
126        }
127
128        public static String getParameterPartValueAsString(FhirContext theCtx, IBase theParameter, String theParameterName) {
129                return getParameterPartValue(theCtx, theParameter, theParameterName).map(t -> (IPrimitiveType<?>) t).map(t -> t.getValueAsString()).orElse(null);
130        }
131
132        private static <T> List<T> extractNamedParameters(FhirContext theCtx, IBaseParameters theParameters, String theParameterName, Function<IPrimitiveType<?>, T> theMapper) {
133                List<T> retVal = new ArrayList<>();
134
135                List<IBase> namedParameters = getNamedParameters(theCtx, theParameters, theParameterName);
136                for (IBase nextParameter : namedParameters) {
137                        BaseRuntimeElementCompositeDefinition<?> nextParameterDef = (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(nextParameter.getClass());
138                        BaseRuntimeChildDefinition valueChild = nextParameterDef.getChildByName("value[x]");
139                        List<IBase> valueValues = valueChild.getAccessor().getValues(nextParameter);
140                        valueValues
141                                .stream()
142                                .filter(t -> t instanceof IPrimitiveType<?>)
143                                .map(t -> ((IPrimitiveType<?>) t))
144                                .map(theMapper)
145                                .filter(t -> t != null)
146                                .forEach(retVal::add);
147
148                }
149                return retVal;
150        }
151
152        private static void addClientParameter(FhirContext theContext, Object theValue, IBaseResource theTargetResource, BaseRuntimeChildDefinition paramChild, BaseRuntimeElementCompositeDefinition<?> paramChildElem, String theName) {
153                Validate.notNull(theValue, "theValue must not be null");
154
155                if (theValue instanceof IBaseResource) {
156                        IBase parameter = createParameterRepetition(theContext, theTargetResource, paramChild, paramChildElem, theName);
157                        paramChildElem.getChildByName("resource").getMutator().addValue(parameter, (IBaseResource) theValue);
158                } else if (theValue instanceof IBaseDatatype) {
159                        IBase parameter = createParameterRepetition(theContext, theTargetResource, paramChild, paramChildElem, theName);
160                        paramChildElem.getChildByName("value[x]").getMutator().addValue(parameter, (IBaseDatatype) theValue);
161                } else if (theValue instanceof Collection) {
162                        Collection<?> collection = (Collection<?>) theValue;
163                        for (Object next : collection) {
164                                addClientParameter(theContext, next, theTargetResource, paramChild, paramChildElem, theName);
165                        }
166                } else {
167                        throw new IllegalArgumentException(Msg.code(1806) + "Don't know how to handle value of type " + theValue.getClass() + " for parameter " + theName);
168                }
169        }
170
171        /**
172         * Add a parameter value to a Parameters resource
173         *
174         * @param theContext    The FhirContext
175         * @param theParameters The Parameters resource
176         * @param theName       The parametr name
177         * @param theValue      The parameter value (can be a {@link IBaseResource resource} or a {@link IBaseDatatype datatype})
178         */
179        public static void addParameterToParameters(FhirContext theContext, IBaseParameters theParameters, String theName, Object theValue) {
180                RuntimeResourceDefinition def = theContext.getResourceDefinition(theParameters);
181                BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter");
182                BaseRuntimeElementCompositeDefinition<?> paramChildElem = (BaseRuntimeElementCompositeDefinition<?>) paramChild.getChildByName("parameter");
183
184                addClientParameter(theContext, theValue, theParameters, paramChild, paramChildElem, theName);
185        }
186
187        /**
188         * Add a parameter value to a Parameters resource
189         *
190         * @param theContext           The FhirContext
191         * @param theParameters        The Parameters resource
192         * @param theName              The parameter name
193         * @param thePrimitiveDatatype The datatype, e.g. "string", or "uri"
194         * @param theValue             The value
195         */
196        public static void addParameterToParameters(FhirContext theContext, IBaseParameters theParameters, String theName, String thePrimitiveDatatype, String theValue) {
197                Validate.notBlank(thePrimitiveDatatype, "thePrimitiveDatatype must not be null or empty");
198
199                BaseRuntimeElementDefinition<?> datatypeDef = theContext.getElementDefinition(thePrimitiveDatatype);
200                IPrimitiveType<?> value = (IPrimitiveType<?>) datatypeDef.newInstance();
201                value.setValueAsString(theValue);
202
203                addParameterToParameters(theContext, theParameters, theName, value);
204        }
205
206        private static IBase createParameterRepetition(FhirContext theContext, IBaseResource theTargetResource, BaseRuntimeChildDefinition paramChild, BaseRuntimeElementCompositeDefinition<?> paramChildElem, String theName) {
207                IBase parameter = paramChildElem.newInstance();
208                paramChild.getMutator().addValue(theTargetResource, parameter);
209                IPrimitiveType<?> value;
210                value = createString(theContext, theName);
211                paramChildElem.getChildByName("name").getMutator().addValue(parameter, value);
212                return parameter;
213        }
214
215        public static IPrimitiveType<?> createString(FhirContext theContext, String theValue) {
216                IPrimitiveType<?> value;
217                if (theContext.getVersion().getVersion().isRi()) {
218                        value = (IPrimitiveType<?>) theContext.getElementDefinition("string").newInstance(theValue);
219                } else {
220                        value = new StringDt(theValue);
221                }
222                return value;
223        }
224
225        public static IPrimitiveType<?> createUri(FhirContext theContext, String theValue) {
226                IPrimitiveType<?> value = (IPrimitiveType<?>) theContext.getElementDefinition("uri").newInstance(theValue);
227                return value;
228        }
229
230        public static IPrimitiveType<?> createCode(FhirContext theContext, String theValue) {
231                IPrimitiveType<?> value = (IPrimitiveType<?>) theContext.getElementDefinition("code").newInstance(theValue);
232                return value;
233        }
234
235        public static IBaseParameters newInstance(FhirContext theContext) {
236                Validate.notNull(theContext, "theContext must not be null");
237                return (IBaseParameters) theContext.getResourceDefinition("Parameters").newInstance();
238        }
239
240        @SuppressWarnings("unchecked")
241        public static void addParameterToParametersBoolean(FhirContext theCtx, IBaseParameters theParameters, String theName, boolean theValue) {
242                addParameterToParameters(theCtx, theParameters, theName, theCtx.getPrimitiveBoolean(theValue));
243        }
244
245        @SuppressWarnings("unchecked")
246        public static void addParameterToParametersCode(FhirContext theCtx, IBaseParameters theParameters, String theName, String theValue) {
247                IPrimitiveType<String> value = (IPrimitiveType<String>) theCtx.getElementDefinition("code").newInstance();
248                value.setValue(theValue);
249                addParameterToParameters(theCtx, theParameters, theName, value);
250        }
251
252        @SuppressWarnings("unchecked")
253        public static void addParameterToParametersInteger(FhirContext theCtx, IBaseParameters theParameters, String theName, int theValue) {
254                IPrimitiveType<Integer> count = (IPrimitiveType<Integer>) theCtx.getElementDefinition("integer").newInstance();
255                count.setValue(theValue);
256                addParameterToParameters(theCtx, theParameters, theName, count);
257        }
258
259        public static void addParameterToParametersLong(FhirContext theCtx, IBaseParameters theParameters, String theName, long theValue) {
260                addParameterToParametersDecimal(theCtx, theParameters, theName, BigDecimal.valueOf(theValue));
261        }
262
263        public static void addParameterToParametersDecimal(FhirContext theCtx, IBaseParameters theParameters, String theName, BigDecimal theValue) {
264                IPrimitiveType<BigDecimal> count = (IPrimitiveType<BigDecimal>) theCtx.getElementDefinition("decimal").newInstance();
265                count.setValue(theValue);
266                addParameterToParameters(theCtx, theParameters, theName, count);
267        }
268
269        public static void addParameterToParametersReference(FhirContext theCtx, IBaseParameters theParameters, String theName, String theReference) {
270                IBaseReference target = (IBaseReference) theCtx.getElementDefinition("reference").newInstance();
271                target.setReference(theReference);
272                addParameterToParameters(theCtx, theParameters, theName, target);
273        }
274
275        @SuppressWarnings("unchecked")
276        public static void addParameterToParametersString(FhirContext theCtx, IBaseParameters theParameters, String theName, String theValue) {
277                IPrimitiveType<String> value = (IPrimitiveType<String>) theCtx.getElementDefinition("string").newInstance();
278                value.setValue(theValue);
279                addParameterToParameters(theCtx, theParameters, theName, value);
280        }
281
282        @SuppressWarnings("unchecked")
283        public static void addParameterToParametersUri(FhirContext theCtx, IBaseParameters theParameters, String theName, String theValue) {
284                IPrimitiveType<String> value = (IPrimitiveType<String>) theCtx.getElementDefinition("uri").newInstance();
285                value.setValue(theValue);
286                addParameterToParameters(theCtx, theParameters, theName, value);
287
288        }
289
290        /**
291         * Add a parameter with no value (typically because we'll be adding sub-parameters)
292         */
293        public static IBase addParameterToParameters(FhirContext theContext, IBaseParameters theParameters, String theName) {
294                RuntimeResourceDefinition def = theContext.getResourceDefinition(theParameters);
295                BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter");
296                BaseRuntimeElementCompositeDefinition<?> paramChildElem = (BaseRuntimeElementCompositeDefinition<?>) paramChild.getChildByName("parameter");
297
298                return createParameterRepetition(theContext, theParameters, paramChild, paramChildElem, theName);
299        }
300
301        public static void addPartCode(FhirContext theContext, IBase theParameter, String theName, String theCode) {
302                IPrimitiveType<String> value = (IPrimitiveType<String>) theContext.getElementDefinition("code").newInstance();
303                value.setValue(theCode);
304
305                addPart(theContext, theParameter, theName, value);
306        }
307
308        public static void addPartInteger(FhirContext theContext, IBase theParameter, String theName, Integer theInteger) {
309                IPrimitiveType<Integer> value = (IPrimitiveType<Integer>) theContext.getElementDefinition("integer").newInstance();
310                value.setValue(theInteger);
311
312                addPart(theContext, theParameter, theName, value);
313        }
314
315        public static void addPartString(FhirContext theContext, IBase theParameter, String theName, String theValue) {
316                IPrimitiveType<String> value = (IPrimitiveType<String>) theContext.getElementDefinition("string").newInstance();
317                value.setValue(theValue);
318
319                addPart(theContext, theParameter, theName, value);
320        }
321
322        public static void addPartUrl(FhirContext theContext, IBase theParameter, String theName, String theCode) {
323                IPrimitiveType<String> value = (IPrimitiveType<String>) theContext.getElementDefinition("url").newInstance();
324                value.setValue(theCode);
325
326                addPart(theContext, theParameter, theName, value);
327        }
328
329        public static void addPartBoolean(FhirContext theContext, IBase theParameter, String theName, Boolean theValue) {
330                addPart(theContext, theParameter, theName, theContext.getPrimitiveBoolean(theValue));
331        }
332
333        public static void addPartDecimal(FhirContext theContext, IBase theParameter, String theName, Double theValue) {
334                IPrimitiveType<BigDecimal> value = (IPrimitiveType<BigDecimal>) theContext.getElementDefinition("decimal").newInstance();
335                value.setValue(theValue == null ? null : new BigDecimal(theValue));
336
337                addPart(theContext, theParameter, theName, value);
338        }
339
340        public static void addPartCoding(FhirContext theContext, IBase theParameter, String theName, String theSystem, String theCode, String theDisplay) {
341                IBase coding = theContext.getElementDefinition("coding").newInstance();
342
343                BaseRuntimeElementCompositeDefinition<?> codingDef = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(coding.getClass());
344                codingDef.getChildByName("system").getMutator().addValue(coding, createUri(theContext, theSystem));
345                codingDef.getChildByName("code").getMutator().addValue(coding, createCode(theContext, theCode));
346                codingDef.getChildByName("display").getMutator().addValue(coding, createString(theContext, theDisplay));
347
348                addPart(theContext, theParameter, theName, coding);
349        }
350
351        public static void addPart(FhirContext theContext, IBase theParameter, String theName, IBase theValue) {
352                BaseRuntimeElementCompositeDefinition<?> def = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theParameter.getClass());
353                BaseRuntimeChildDefinition partChild = def.getChildByName("part");
354
355                BaseRuntimeElementCompositeDefinition<?> partChildElem = (BaseRuntimeElementCompositeDefinition<?>) partChild.getChildByName("part");
356                IBase part = partChildElem.newInstance();
357                partChild.getMutator().addValue(theParameter, part);
358
359                IPrimitiveType<String> name = (IPrimitiveType<String>) theContext.getElementDefinition("string").newInstance();
360                name.setValue(theName);
361                partChildElem.getChildByName("name").getMutator().addValue(part, name);
362
363                if (theValue instanceof IBaseResource) {
364                        partChildElem.getChildByName("resource").getMutator().addValue(part, theValue);
365                } else {
366                        partChildElem.getChildByName("value[x]").getMutator().addValue(part, theValue);
367                }
368        }
369
370        public static void addPartResource(FhirContext theContext, IBase theParameter, String theName, IBaseResource theValue) {
371                BaseRuntimeElementCompositeDefinition<?> def = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theParameter.getClass());
372                BaseRuntimeChildDefinition partChild = def.getChildByName("part");
373
374                BaseRuntimeElementCompositeDefinition<?> partChildElem = (BaseRuntimeElementCompositeDefinition<?>) partChild.getChildByName("part");
375                IBase part = partChildElem.newInstance();
376                partChild.getMutator().addValue(theParameter, part);
377
378                IPrimitiveType<String> name = (IPrimitiveType<String>) theContext.getElementDefinition("string").newInstance();
379                name.setValue(theName);
380                partChildElem.getChildByName("name").getMutator().addValue(part, name);
381
382                partChildElem.getChildByName("resource").getMutator().addValue(part, theValue);
383        }
384
385        public static List<String> getNamedParameterPartAsString(FhirContext theCtx, IBaseParameters theParameters, String thePartName, String theParameterName) {
386                return extractNamedParameterPartsAsString(theCtx, theParameters, thePartName, theParameterName);
387        }
388
389        // TODO KHS need to consolidate duplicated functionality that came in from different branches
390        private static List<String> extractNamedParameterPartsAsString(FhirContext theCtx, IBaseParameters theParameters, String thePartName, String theParameterName) {
391                List<IBase> parameterReps = getParameterReps(theCtx, theParameters);
392
393                List<String> retVal = new ArrayList<>();
394
395                for (IBase nextParameter : parameterReps) {
396                        BaseRuntimeElementCompositeDefinition<?> nextParameterDef = (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(nextParameter.getClass());
397                        Optional<? extends IPrimitiveType<?>> nameValue = getNameValue(nextParameter, nextParameterDef);
398                        if (!nameValue.isPresent() || !thePartName.equals(nameValue.get().getValueAsString())) {
399                                continue;
400                        }
401
402                        BaseRuntimeChildDefinition partChild = nextParameterDef.getChildByName("part");
403                        List<IBase> partValues = partChild.getAccessor().getValues(nextParameter);
404                        for (IBase partValue : partValues) {
405                                BaseRuntimeElementCompositeDefinition<?> partParameterDef = (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(partValue.getClass());
406                                Optional<? extends IPrimitiveType<?>> partNameValue = getNameValue(partValue, partParameterDef);
407                                if (!partNameValue.isPresent() || !theParameterName.equals(partNameValue.get().getValueAsString())) {
408                                        continue;
409                                }
410                                BaseRuntimeChildDefinition valueChild = partParameterDef.getChildByName("value[x]");
411                                List<IBase> valueValues = valueChild.getAccessor().getValues(partValue);
412                                valueValues
413                                        .stream()
414                                        .filter(t -> t instanceof IPrimitiveType<?>)
415                                        .map(t -> ((IPrimitiveType<String>) t))
416                                        .map(t -> defaultIfBlank(t.getValueAsString(), null))
417                                        .filter(t -> t != null)
418                                        .forEach(retVal::add);
419
420                        }
421                }
422                return retVal;
423        }
424
425        private static List<IBase> getParameterReps(FhirContext theCtx, IBaseParameters theParameters) {
426                Validate.notNull(theParameters, "theParameters must not be null");
427                RuntimeResourceDefinition resDef = theCtx.getResourceDefinition(theParameters.getClass());
428                BaseRuntimeChildDefinition parameterChild = resDef.getChildByName("parameter");
429                return parameterChild.getAccessor().getValues(theParameters);
430        }
431
432        private static Optional<? extends IPrimitiveType<?>> getNameValue(IBase nextParameter, BaseRuntimeElementCompositeDefinition<?> theNextParameterDef) {
433                BaseRuntimeChildDefinition nameChild = theNextParameterDef.getChildByName("name");
434                List<IBase> nameValues = nameChild.getAccessor().getValues(nextParameter);
435                return nameValues
436                        .stream()
437                        .filter(t -> t instanceof IPrimitiveType<?>)
438                        .map(t -> ((IPrimitiveType<?>) t))
439                        .findFirst();
440        }
441
442        @Nullable
443        public static String extractDescription(AnnotatedElement theType) {
444                Description description = theType.getAnnotation(Description.class);
445                if (description != null) {
446                        return extractDescription(description);
447                } else {
448                        return null;
449                }
450        }
451
452        @Nullable
453        public static String extractDescription(Description desc) {
454                String description = desc.value();
455                if (isBlank(description)) {
456                        description = desc.formalDefinition();
457                }
458                if (isBlank(description)) {
459                        description = desc.shortDefinition();
460                }
461                return defaultIfBlank(description, null);
462        }
463
464        @Nullable
465        public static String extractShortDefinition(AnnotatedElement theType) {
466                Description description = theType.getAnnotation(Description.class);
467                if (description != null) {
468                        return defaultIfBlank(description.shortDefinition(), null);
469                } else {
470                        return null;
471                }
472        }
473
474        public static String extractDescription(Annotation[] theParameterAnnotations) {
475                for (Annotation next : theParameterAnnotations) {
476                        if (next instanceof Description) {
477                                return extractDescription((Description)next);
478                        }
479                }
480                return null;
481        }
482
483        public static List<String> extractExamples(Annotation[] theParameterAnnotations) {
484                ArrayList<String> retVal = null;
485                for (Annotation next : theParameterAnnotations) {
486                        if (next instanceof Description) {
487                                String[] examples = ((Description) next).example();
488                                if (examples.length > 0) {
489                                        if (retVal == null) {
490                                                retVal = new ArrayList<>();
491                                        }
492                                        retVal.addAll(Arrays.asList(examples));
493                                }
494                        }
495                }
496                return retVal;
497        }
498}