
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 /** 238 * @since 8.6.0 239 */ 240 public static Optional<Boolean> getParameterPartValueAsBoolean( 241 FhirContext theCtx, IBase theParameter, String theParameterName) { 242 return getParameterPartValue(theCtx, theParameter, theParameterName) 243 .filter(t -> IPrimitiveType.class.isAssignableFrom(t.getClass())) 244 .map(t -> (IPrimitiveType<?>) t) 245 .map(IPrimitiveType::getValue) 246 .filter(t -> Boolean.class.isAssignableFrom(t.getClass())) 247 .map(t -> (Boolean) t); 248 } 249 250 private static <T> List<T> extractNamedParameterValues( 251 FhirContext theCtx, 252 IBaseParameters theParameters, 253 String theParameterName, 254 Function<IPrimitiveType<?>, T> theMapper) { 255 List<T> retVal = new ArrayList<>(); 256 257 List<IBase> namedParameters = getNamedParameters(theCtx, theParameters, theParameterName); 258 for (IBase nextParameter : namedParameters) { 259 BaseRuntimeElementCompositeDefinition<?> nextParameterDef = 260 (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(nextParameter.getClass()); 261 BaseRuntimeChildDefinition valueChild = nextParameterDef.getChildByName("value[x]"); 262 List<IBase> valueValues = valueChild.getAccessor().getValues(nextParameter); 263 valueValues.stream() 264 .filter(t -> t instanceof IPrimitiveType<?>) 265 .map(t -> ((IPrimitiveType<?>) t)) 266 .map(theMapper) 267 .filter(Objects::nonNull) 268 .forEach(retVal::add); 269 } 270 return retVal; 271 } 272 273 private static void addClientParameter( 274 FhirContext theContext, 275 Object theValue, 276 IBaseResource theTargetResource, 277 BaseRuntimeChildDefinition paramChild, 278 BaseRuntimeElementCompositeDefinition<?> paramChildElem, 279 String theName) { 280 Validate.notNull(theValue, "theValue must not be null"); 281 282 if (theValue instanceof IBaseResource) { 283 IBase parameter = 284 createParameterRepetition(theContext, theTargetResource, paramChild, paramChildElem, theName); 285 paramChildElem.getChildByName("resource").getMutator().addValue(parameter, (IBaseResource) theValue); 286 } else if (theValue instanceof IBaseDatatype) { 287 IBase parameter = 288 createParameterRepetition(theContext, theTargetResource, paramChild, paramChildElem, theName); 289 paramChildElem.getChildByName("value[x]").getMutator().addValue(parameter, (IBaseDatatype) theValue); 290 } else if (theValue instanceof Collection<?> collection) { 291 for (Object next : collection) { 292 addClientParameter(theContext, next, theTargetResource, paramChild, paramChildElem, theName); 293 } 294 } else { 295 throw new IllegalArgumentException(Msg.code(1806) + "Don't know how to handle value of type " 296 + theValue.getClass() + " for parameter " + theName); 297 } 298 } 299 300 /** 301 * Add a parameter value to a Parameters resource 302 * 303 * @param theContext The FhirContext 304 * @param theParameters The Parameters resource 305 * @param theName The parameter name 306 * @param theValue The parameter value (can be a {@link IBaseResource resource} or a {@link IBaseDatatype datatype}) 307 */ 308 public static void addParameterToParameters( 309 FhirContext theContext, IBaseParameters theParameters, String theName, Object theValue) { 310 RuntimeResourceDefinition def = theContext.getResourceDefinition(theParameters); 311 BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter"); 312 BaseRuntimeElementCompositeDefinition<?> paramChildElem = 313 (BaseRuntimeElementCompositeDefinition<?>) paramChild.getChildByName("parameter"); 314 315 addClientParameter(theContext, theValue, theParameters, paramChild, paramChildElem, theName); 316 } 317 318 /** 319 * Add a parameter value to a Parameters resource 320 * 321 * @param theContext The FhirContext 322 * @param theParameters The Parameters resource 323 * @param theName The parameter name 324 * @param thePrimitiveDatatype The datatype, e.g. "string", or "uri" 325 * @param theValue The value 326 */ 327 public static void addParameterToParameters( 328 FhirContext theContext, 329 IBaseParameters theParameters, 330 String theName, 331 String thePrimitiveDatatype, 332 String theValue) { 333 Validate.notBlank(thePrimitiveDatatype, "thePrimitiveDatatype must not be null or empty"); 334 335 BaseRuntimeElementDefinition<?> datatypeDef = theContext.getElementDefinition(thePrimitiveDatatype); 336 assert datatypeDef != null; 337 IPrimitiveType<?> value = (IPrimitiveType<?>) datatypeDef.newInstance(); 338 value.setValueAsString(theValue); 339 340 addParameterToParameters(theContext, theParameters, theName, value); 341 } 342 343 private static IBase createParameterRepetition( 344 FhirContext theContext, 345 IBase theTargetResource, 346 BaseRuntimeChildDefinition paramChild, 347 BaseRuntimeElementCompositeDefinition<?> paramChildElem, 348 String theName) { 349 IBase parameter = paramChildElem.newInstance(); 350 paramChild.getMutator().addValue(theTargetResource, parameter); 351 IPrimitiveType<?> value; 352 value = createString(theContext, theName); 353 paramChildElem.getChildByName("name").getMutator().addValue(parameter, value); 354 return parameter; 355 } 356 357 public static IPrimitiveType<?> createString(FhirContext theContext, String theValue) { 358 IPrimitiveType<?> value; 359 if (theContext.getVersion().getVersion().isRi()) { 360 value = (IPrimitiveType<?>) Objects.requireNonNull(theContext.getElementDefinition("string")) 361 .newInstance(theValue); 362 } else { 363 value = new StringDt(theValue); 364 } 365 return value; 366 } 367 368 public static IPrimitiveType<?> createUri(FhirContext theContext, String theValue) { 369 return (IPrimitiveType<?>) 370 Objects.requireNonNull(theContext.getElementDefinition("uri")).newInstance(theValue); 371 } 372 373 public static IPrimitiveType<?> createCode(FhirContext theContext, String theValue) { 374 return (IPrimitiveType<?>) 375 Objects.requireNonNull(theContext.getElementDefinition("code")).newInstance(theValue); 376 } 377 378 public static IPrimitiveType<?> createInstant(FhirContext theContext, Date theValue) { 379 return (IPrimitiveType<?>) Objects.requireNonNull(theContext.getElementDefinition("instant")) 380 .newInstance(theValue); 381 } 382 383 public static IBaseParameters newInstance(FhirContext theContext) { 384 Validate.notNull(theContext, "theContext must not be null"); 385 return (IBaseParameters) theContext.getResourceDefinition("Parameters").newInstance(); 386 } 387 388 public static void addParameterToParametersBoolean( 389 FhirContext theCtx, IBaseParameters theParameters, String theName, boolean theValue) { 390 addParameterToParameters(theCtx, theParameters, theName, theCtx.newPrimitiveBoolean(theValue)); 391 } 392 393 @SuppressWarnings("unchecked") 394 public static void addParameterToParametersCode( 395 FhirContext theCtx, IBaseParameters theParameters, String theName, String theValue) { 396 IPrimitiveType<String> value = (IPrimitiveType<String>) 397 Objects.requireNonNull(theCtx.getElementDefinition("code")).newInstance(); 398 value.setValue(theValue); 399 addParameterToParameters(theCtx, theParameters, theName, value); 400 } 401 402 @SuppressWarnings("unchecked") 403 public static void addParameterToParametersInteger( 404 FhirContext theCtx, IBaseParameters theParameters, String theName, int theValue) { 405 IPrimitiveType<Integer> count = (IPrimitiveType<Integer>) 406 Objects.requireNonNull(theCtx.getElementDefinition("integer")).newInstance(); 407 count.setValue(theValue); 408 addParameterToParameters(theCtx, theParameters, theName, count); 409 } 410 411 public static void addParameterToParametersLong( 412 FhirContext theCtx, IBaseParameters theParameters, String theName, long theValue) { 413 addParameterToParametersDecimal(theCtx, theParameters, theName, BigDecimal.valueOf(theValue)); 414 } 415 416 public static void addParameterToParametersDecimal( 417 FhirContext theCtx, IBaseParameters theParameters, String theName, BigDecimal theValue) { 418 IPrimitiveType<BigDecimal> count = (IPrimitiveType<BigDecimal>) 419 Objects.requireNonNull(theCtx.getElementDefinition("decimal")).newInstance(); 420 count.setValue(theValue); 421 addParameterToParameters(theCtx, theParameters, theName, count); 422 } 423 424 public static void addParameterToParametersReference( 425 FhirContext theCtx, IBaseParameters theParameters, String theName, String theReference) { 426 IBaseReference target = (IBaseReference) 427 Objects.requireNonNull(theCtx.getElementDefinition("reference")).newInstance(); 428 target.setReference(theReference); 429 addParameterToParameters(theCtx, theParameters, theName, target); 430 } 431 432 @SuppressWarnings("unchecked") 433 public static void addParameterToParametersString( 434 FhirContext theCtx, IBaseParameters theParameters, String theName, String theValue) { 435 IPrimitiveType<String> value = (IPrimitiveType<String>) 436 Objects.requireNonNull(theCtx.getElementDefinition("string")).newInstance(); 437 value.setValue(theValue); 438 addParameterToParameters(theCtx, theParameters, theName, value); 439 } 440 441 @SuppressWarnings("unchecked") 442 public static void addParameterToParametersUri( 443 FhirContext theCtx, IBaseParameters theParameters, String theName, String theValue) { 444 IPrimitiveType<String> value = (IPrimitiveType<String>) 445 Objects.requireNonNull(theCtx.getElementDefinition("uri")).newInstance(); 446 value.setValue(theValue); 447 addParameterToParameters(theCtx, theParameters, theName, value); 448 } 449 450 /** 451 * Add a parameter with no value (typically because we'll be adding sub-parameters) 452 */ 453 public static IBase addParameterToParameters( 454 FhirContext theContext, IBaseParameters theParameters, String theName) { 455 RuntimeResourceDefinition def = theContext.getResourceDefinition(theParameters); 456 BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter"); 457 BaseRuntimeElementCompositeDefinition<?> paramChildElem = 458 (BaseRuntimeElementCompositeDefinition<?>) paramChild.getChildByName("parameter"); 459 460 return createParameterRepetition(theContext, theParameters, paramChild, paramChildElem, theName); 461 } 462 463 public static void addPartCode(FhirContext theContext, IBase theParameter, String theName, String theCode) { 464 IPrimitiveType<String> value = (IPrimitiveType<String>) 465 Objects.requireNonNull(theContext.getElementDefinition("code")).newInstance(); 466 value.setValue(theCode); 467 468 addPart(theContext, theParameter, theName, value); 469 } 470 471 public static void addPartInteger(FhirContext theContext, IBase theParameter, String theName, Integer theInteger) { 472 IPrimitiveType<Integer> value = 473 (IPrimitiveType<Integer>) Objects.requireNonNull(theContext.getElementDefinition("integer")) 474 .newInstance(); 475 value.setValue(theInteger); 476 477 addPart(theContext, theParameter, theName, value); 478 } 479 480 public static void addPartString(FhirContext theContext, IBase theParameter, String theName, String theValue) { 481 IPrimitiveType<String> value = 482 (IPrimitiveType<String>) Objects.requireNonNull(theContext.getElementDefinition("string")) 483 .newInstance(); 484 value.setValue(theValue); 485 486 addPart(theContext, theParameter, theName, value); 487 } 488 489 public static void addPartUrl(FhirContext theContext, IBase theParameter, String theName, String theCode) { 490 IPrimitiveType<String> value = (IPrimitiveType<String>) 491 Objects.requireNonNull(theContext.getElementDefinition("url")).newInstance(); 492 value.setValue(theCode); 493 494 addPart(theContext, theParameter, theName, value); 495 } 496 497 /** 498 * @return Returns the added part 499 */ 500 public static IBase addPartBoolean(FhirContext theContext, IBase theParameter, String theName, Boolean theValue) { 501 return addPart(theContext, theParameter, theName, theContext.newPrimitiveBoolean(theValue)); 502 } 503 504 public static void addPartDecimal(FhirContext theContext, IBase theParameter, String theName, Double theValue) { 505 IPrimitiveType<BigDecimal> value = (IPrimitiveType<BigDecimal>) 506 theContext.getElementDefinition("decimal").newInstance(); 507 if (theValue == null) { 508 value.setValue(null); 509 } else { 510 BigDecimal decimalValue = BigDecimal.valueOf(theValue); 511 if (decimalValue.scale() < 0) { 512 decimalValue = decimalValue.setScale(0); 513 } 514 value.setValue(decimalValue); 515 } 516 addPart(theContext, theParameter, theName, value); 517 } 518 519 public static void addPartCoding( 520 FhirContext theContext, 521 IBase theParameter, 522 String theName, 523 String theSystem, 524 String theCode, 525 String theDisplay) { 526 IBase coding = Objects.requireNonNull(theContext.getElementDefinition("coding")) 527 .newInstance(); 528 529 BaseRuntimeElementCompositeDefinition<?> codingDef = 530 (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(coding.getClass()); 531 codingDef.getChildByName("system").getMutator().addValue(coding, createUri(theContext, theSystem)); 532 codingDef.getChildByName("code").getMutator().addValue(coding, createCode(theContext, theCode)); 533 codingDef.getChildByName("display").getMutator().addValue(coding, createString(theContext, theDisplay)); 534 535 addPart(theContext, theParameter, theName, coding); 536 } 537 538 public static IBase addPart(FhirContext theContext, IBase theParameter, String theName, @Nullable IBase theValue) { 539 BaseRuntimeElementCompositeDefinition<?> def = 540 (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theParameter.getClass()); 541 BaseRuntimeChildDefinition partChild = def.getChildByName("part"); 542 543 BaseRuntimeElementCompositeDefinition<?> partChildElem = 544 (BaseRuntimeElementCompositeDefinition<?>) partChild.getChildByName("part"); 545 IBase part = partChildElem.newInstance(); 546 partChild.getMutator().addValue(theParameter, part); 547 548 IPrimitiveType<String> name = (IPrimitiveType<String>) 549 theContext.getElementDefinition("string").newInstance(); 550 name.setValue(theName); 551 partChildElem.getChildByName("name").getMutator().addValue(part, name); 552 553 if (theValue != null) { 554 if (theValue instanceof IBaseResource) { 555 partChildElem.getChildByName("resource").getMutator().addValue(part, theValue); 556 } else { 557 partChildElem.getChildByName("value[x]").getMutator().addValue(part, theValue); 558 } 559 } 560 return part; 561 } 562 563 public static IBase createPart(FhirContext theContext, IBase thePart, String theName) { 564 BaseRuntimeElementCompositeDefinition<?> def = 565 (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(thePart.getClass()); 566 BaseRuntimeChildDefinition partChild = def.getChildByName("part"); 567 568 BaseRuntimeElementCompositeDefinition<?> partChildElem = 569 (BaseRuntimeElementCompositeDefinition<?>) partChild.getChildByName("part"); 570 571 return createParameterRepetition(theContext, thePart, partChild, partChildElem, theName); 572 } 573 574 public static void addPartResource( 575 FhirContext theContext, IBase theParameter, String theName, IBaseResource theValue) { 576 BaseRuntimeElementCompositeDefinition<?> def = 577 (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theParameter.getClass()); 578 BaseRuntimeChildDefinition partChild = def.getChildByName("part"); 579 580 BaseRuntimeElementCompositeDefinition<?> partChildElem = 581 (BaseRuntimeElementCompositeDefinition<?>) partChild.getChildByName("part"); 582 IBase part = partChildElem.newInstance(); 583 partChild.getMutator().addValue(theParameter, part); 584 585 IPrimitiveType<String> name = (IPrimitiveType<String>) 586 theContext.getElementDefinition("string").newInstance(); 587 name.setValue(theName); 588 partChildElem.getChildByName("name").getMutator().addValue(part, name); 589 590 partChildElem.getChildByName("resource").getMutator().addValue(part, theValue); 591 } 592 593 public static List<String> getNamedParameterPartAsString( 594 FhirContext theCtx, IBaseParameters theParameters, String thePartName, String theParameterName) { 595 return extractNamedParameterPartsAsString(theCtx, theParameters, thePartName, theParameterName); 596 } 597 598 // TODO KHS need to consolidate duplicated functionality that came in from different branches 599 private static List<String> extractNamedParameterPartsAsString( 600 FhirContext theCtx, IBaseParameters theParameters, String thePartName, String theParameterName) { 601 List<IBase> parameterReps = getParameterReps(theCtx, theParameters); 602 603 List<String> retVal = new ArrayList<>(); 604 605 for (IBase nextParameter : parameterReps) { 606 BaseRuntimeElementCompositeDefinition<?> nextParameterDef = 607 (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(nextParameter.getClass()); 608 Optional<? extends IPrimitiveType<?>> nameValue = getNameValue(nextParameter, nextParameterDef); 609 if (nameValue.isEmpty() || !thePartName.equals(nameValue.get().getValueAsString())) { 610 continue; 611 } 612 613 BaseRuntimeChildDefinition partChild = nextParameterDef.getChildByName("part"); 614 List<IBase> partValues = partChild.getAccessor().getValues(nextParameter); 615 for (IBase partValue : partValues) { 616 BaseRuntimeElementCompositeDefinition<?> partParameterDef = 617 (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(partValue.getClass()); 618 Optional<? extends IPrimitiveType<?>> partNameValue = getNameValue(partValue, partParameterDef); 619 if (partNameValue.isEmpty() 620 || !theParameterName.equals(partNameValue.get().getValueAsString())) { 621 continue; 622 } 623 BaseRuntimeChildDefinition valueChild = partParameterDef.getChildByName("value[x]"); 624 List<IBase> valueValues = valueChild.getAccessor().getValues(partValue); 625 valueValues.stream() 626 .filter(t -> t instanceof IPrimitiveType<?>) 627 .map(t -> ((IPrimitiveType<String>) t)) 628 .map(t -> defaultIfBlank(t.getValueAsString(), null)) 629 .filter(Objects::nonNull) 630 .forEach(retVal::add); 631 } 632 } 633 return retVal; 634 } 635 636 private static List<IBase> getParameterReps(FhirContext theCtx, IBaseParameters theParameters) { 637 Validate.notNull(theParameters, "theParameters must not be null"); 638 RuntimeResourceDefinition resDef = theCtx.getResourceDefinition(theParameters.getClass()); 639 BaseRuntimeChildDefinition parameterChild = resDef.getChildByName("parameter"); 640 return parameterChild.getAccessor().getValues(theParameters); 641 } 642 643 private static Optional<? extends IPrimitiveType<?>> getNameValue( 644 IBase nextParameter, BaseRuntimeElementCompositeDefinition<?> theNextParameterDef) { 645 BaseRuntimeChildDefinition nameChild = theNextParameterDef.getChildByName("name"); 646 List<IBase> nameValues = nameChild.getAccessor().getValues(nextParameter); 647 return nameValues.stream() 648 .filter(t -> t instanceof IPrimitiveType<?>) 649 .map(t -> ((IPrimitiveType<?>) t)) 650 .findFirst(); 651 } 652 653 @Nullable 654 public static String extractDescription(AnnotatedElement theType) { 655 Description description = theType.getAnnotation(Description.class); 656 if (description != null) { 657 return extractDescription(description); 658 } else { 659 return null; 660 } 661 } 662 663 @Nullable 664 public static String extractDescription(Description desc) { 665 String description = desc.value(); 666 if (isBlank(description)) { 667 description = desc.formalDefinition(); 668 } 669 if (isBlank(description)) { 670 description = desc.shortDefinition(); 671 } 672 return defaultIfBlank(description, null); 673 } 674 675 @Nullable 676 public static String extractShortDefinition(AnnotatedElement theType) { 677 Description description = theType.getAnnotation(Description.class); 678 if (description != null) { 679 return defaultIfBlank(description.shortDefinition(), null); 680 } else { 681 return null; 682 } 683 } 684 685 public static String extractDescription(Annotation[] theParameterAnnotations) { 686 for (Annotation next : theParameterAnnotations) { 687 if (next instanceof Description) { 688 return extractDescription((Description) next); 689 } 690 } 691 return null; 692 } 693 694 public static List<String> extractExamples(Annotation[] theParameterAnnotations) { 695 ArrayList<String> retVal = null; 696 for (Annotation next : theParameterAnnotations) { 697 if (next instanceof Description) { 698 String[] examples = ((Description) next).example(); 699 if (examples.length > 0) { 700 if (retVal == null) { 701 retVal = new ArrayList<>(); 702 } 703 retVal.addAll(Arrays.asList(examples)); 704 } 705 } 706 } 707 return retVal; 708 } 709}