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