
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.FhirContext; 025import org.hl7.fhir.instance.model.api.IBase; 026import org.hl7.fhir.instance.model.api.IBaseBackboneElement; 027import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; 028import org.hl7.fhir.instance.model.api.IBaseResource; 029import org.hl7.fhir.instance.model.api.IPrimitiveType; 030 031import java.util.List; 032import java.util.Map; 033 034/** 035 * FHIR version independent representation of a Bundle Entry. This class can hold 036 * Bundle Entry data from any FHIR version (DSTU3+) and convert it back to the 037 * appropriate Bundle Entry type. 038 */ 039// Created by Sonnet 4 040public class CanonicalBundleEntry { 041 042 private String myFullUrl; 043 private IBaseResource myResource; 044 045 // Request fields 046 private String myRequestMethod; 047 private String myRequestUrl; 048 private String myRequestIfNoneMatch; 049 private String myRequestIfModifiedSince; 050 private String myRequestIfMatch; 051 private String myRequestIfNoneExist; 052 053 // Response fields 054 private String myResponseStatus; 055 private String myResponseLocation; 056 private String myResponseEtag; 057 private String myResponseLastModified; 058 059 // Search fields 060 private String mySearchMode; 061 private String mySearchScore; 062 063 // Link fields (less common but part of spec) 064 private List<Map<String, String>> myLinks; 065 private IBaseOperationOutcome myResponseOutcome; 066 067 public CanonicalBundleEntry() { 068 // Default constructor 069 } 070 071 public String getFullUrl() { 072 return myFullUrl; 073 } 074 075 public void setFullUrl(String theFullUrl) { 076 myFullUrl = theFullUrl; 077 } 078 079 public IBaseResource getResource() { 080 return myResource; 081 } 082 083 public void setResource(IBaseResource theResource) { 084 myResource = theResource; 085 } 086 087 public String getRequestMethod() { 088 return myRequestMethod; 089 } 090 091 public void setRequestMethod(String theRequestMethod) { 092 myRequestMethod = theRequestMethod; 093 } 094 095 public String getRequestUrl() { 096 return myRequestUrl; 097 } 098 099 public void setRequestUrl(String theRequestUrl) { 100 myRequestUrl = theRequestUrl; 101 } 102 103 public String getRequestIfNoneMatch() { 104 return myRequestIfNoneMatch; 105 } 106 107 public void setRequestIfNoneMatch(String theRequestIfNoneMatch) { 108 myRequestIfNoneMatch = theRequestIfNoneMatch; 109 } 110 111 public String getRequestIfModifiedSince() { 112 return myRequestIfModifiedSince; 113 } 114 115 public void setRequestIfModifiedSince(String theRequestIfModifiedSince) { 116 myRequestIfModifiedSince = theRequestIfModifiedSince; 117 } 118 119 public String getRequestIfMatch() { 120 return myRequestIfMatch; 121 } 122 123 public void setRequestIfMatch(String theRequestIfMatch) { 124 myRequestIfMatch = theRequestIfMatch; 125 } 126 127 public String getRequestIfNoneExist() { 128 return myRequestIfNoneExist; 129 } 130 131 public void setRequestIfNoneExist(String theRequestIfNoneExist) { 132 myRequestIfNoneExist = theRequestIfNoneExist; 133 } 134 135 public String getResponseStatus() { 136 return myResponseStatus; 137 } 138 139 public void setResponseStatus(String theResponseStatus) { 140 myResponseStatus = theResponseStatus; 141 } 142 143 public String getResponseLocation() { 144 return myResponseLocation; 145 } 146 147 public void setResponseLocation(String theResponseLocation) { 148 myResponseLocation = theResponseLocation; 149 } 150 151 public String getResponseEtag() { 152 return myResponseEtag; 153 } 154 155 public void setResponseEtag(String theResponseEtag) { 156 myResponseEtag = theResponseEtag; 157 } 158 159 public String getResponseLastModified() { 160 return myResponseLastModified; 161 } 162 163 public void setResponseLastModified(String theResponseLastModified) { 164 myResponseLastModified = theResponseLastModified; 165 } 166 167 public String getSearchMode() { 168 return mySearchMode; 169 } 170 171 public void setSearchMode(String theSearchMode) { 172 mySearchMode = theSearchMode; 173 } 174 175 public String getSearchScore() { 176 return mySearchScore; 177 } 178 179 public void setSearchScore(String theSearchScore) { 180 mySearchScore = theSearchScore; 181 } 182 183 public List<Map<String, String>> getLinks() { 184 return myLinks; 185 } 186 187 public void setLinks(List<Map<String, String>> theLinks) { 188 myLinks = theLinks; 189 } 190 191 public void setResponseOutcome(IBaseOperationOutcome theResponseOutcome) { 192 myResponseOutcome = theResponseOutcome; 193 } 194 195 public IBaseOperationOutcome getResponseOutcome() { 196 return myResponseOutcome; 197 } 198 199 /** 200 * Factory method to create a CanonicalBundleEntry from a Bundle Entry 201 * @param theFhirContext The FHIR context 202 * @param theEntry The Bundle Entry to convert 203 * @return A new CanonicalBundleEntry instance 204 */ 205 public static CanonicalBundleEntry fromBundleEntry(FhirContext theFhirContext, IBaseBackboneElement theEntry) { 206 CanonicalBundleEntry retVal = new CanonicalBundleEntry(); 207 208 FhirTerser terser = theFhirContext.newTerser(); 209 210 // Extract fullUrl 211 IPrimitiveType<?> fullUrlElement = terser.getSingleValueOrNull(theEntry, "fullUrl", IPrimitiveType.class); 212 if (fullUrlElement != null) { 213 retVal.setFullUrl(fullUrlElement.getValueAsString()); 214 } 215 216 // Extract resource 217 IBaseResource resource = terser.getSingleValueOrNull(theEntry, "resource", IBaseResource.class); 218 if (resource != null) { 219 retVal.setResource(resource); 220 } 221 222 // Extract request fields 223 IBaseBackboneElement request = terser.getSingleValueOrNull(theEntry, "request", IBaseBackboneElement.class); 224 if (request != null) { 225 IPrimitiveType<?> method = terser.getSingleValueOrNull(request, "method", IPrimitiveType.class); 226 if (method != null) { 227 retVal.setRequestMethod(method.getValueAsString()); 228 } 229 230 IPrimitiveType<?> url = terser.getSingleValueOrNull(request, "url", IPrimitiveType.class); 231 if (url != null) { 232 retVal.setRequestUrl(url.getValueAsString()); 233 } 234 235 IPrimitiveType<?> ifNoneMatch = terser.getSingleValueOrNull(request, "ifNoneMatch", IPrimitiveType.class); 236 if (ifNoneMatch != null) { 237 retVal.setRequestIfNoneMatch(ifNoneMatch.getValueAsString()); 238 } 239 240 IPrimitiveType<?> ifModifiedSince = 241 terser.getSingleValueOrNull(request, "ifModifiedSince", IPrimitiveType.class); 242 if (ifModifiedSince != null) { 243 retVal.setRequestIfModifiedSince(ifModifiedSince.getValueAsString()); 244 } 245 246 IPrimitiveType<?> ifMatch = terser.getSingleValueOrNull(request, "ifMatch", IPrimitiveType.class); 247 if (ifMatch != null) { 248 retVal.setRequestIfMatch(ifMatch.getValueAsString()); 249 } 250 251 IPrimitiveType<?> ifNoneExist = terser.getSingleValueOrNull(request, "ifNoneExist", IPrimitiveType.class); 252 if (ifNoneExist != null) { 253 retVal.setRequestIfNoneExist(ifNoneExist.getValueAsString()); 254 } 255 } 256 257 // Extract response fields 258 IBaseBackboneElement response = terser.getSingleValueOrNull(theEntry, "response", IBaseBackboneElement.class); 259 if (response != null) { 260 IPrimitiveType<?> status = terser.getSingleValueOrNull(response, "status", IPrimitiveType.class); 261 if (status != null) { 262 retVal.setResponseStatus(status.getValueAsString()); 263 } 264 265 IPrimitiveType<?> location = terser.getSingleValueOrNull(response, "location", IPrimitiveType.class); 266 if (location != null) { 267 retVal.setResponseLocation(location.getValueAsString()); 268 } 269 270 IPrimitiveType<?> etag = terser.getSingleValueOrNull(response, "etag", IPrimitiveType.class); 271 if (etag != null) { 272 retVal.setResponseEtag(etag.getValueAsString()); 273 } 274 275 IPrimitiveType<?> lastModified = 276 terser.getSingleValueOrNull(response, "lastModified", IPrimitiveType.class); 277 if (lastModified != null) { 278 retVal.setResponseLastModified(lastModified.getValueAsString()); 279 } 280 281 IBaseOperationOutcome outcome = 282 terser.getSingleValueOrNull(response, "outcome", IBaseOperationOutcome.class); 283 if (outcome != null) { 284 retVal.setResponseOutcome(outcome); 285 } 286 } 287 288 // Extract search fields 289 IBaseBackboneElement search = terser.getSingleValueOrNull(theEntry, "search", IBaseBackboneElement.class); 290 if (search != null) { 291 IPrimitiveType<?> mode = terser.getSingleValueOrNull(search, "mode", IPrimitiveType.class); 292 if (mode != null) { 293 retVal.setSearchMode(mode.getValueAsString()); 294 } 295 296 IPrimitiveType<?> score = terser.getSingleValueOrNull(search, "score", IPrimitiveType.class); 297 if (score != null) { 298 retVal.setSearchScore(score.getValueAsString()); 299 } 300 } 301 302 return retVal; 303 } 304 305 /** 306 * Convert this CanonicalBundleEntry back to a Bundle Entry of the specified type 307 * @param theFhirContext The FHIR context 308 * @param theBundleEntryComponentClass The target Bundle Entry class 309 * @return A new Bundle Entry instance 310 */ 311 public <T extends IBase> T toBundleEntry(FhirContext theFhirContext, Class<T> theBundleEntryComponentClass) { 312 @SuppressWarnings("unchecked") 313 T entry = (T) theFhirContext 314 .getElementDefinition(theBundleEntryComponentClass) 315 .newInstance(); 316 317 // Get the element definition for the Bundle Entry 318 BaseRuntimeElementCompositeDefinition<?> entryDef = (BaseRuntimeElementCompositeDefinition<?>) 319 theFhirContext.getElementDefinition(theBundleEntryComponentClass); 320 321 // Set fullUrl 322 if (myFullUrl != null) { 323 BaseRuntimeChildDefinition fullUrlChild = entryDef.getChildByName("fullUrl"); 324 if (fullUrlChild != null) { 325 IPrimitiveType<?> fullUrlValue = (IPrimitiveType<?>) 326 fullUrlChild.getChildByName("fullUrl").newInstance(); 327 fullUrlValue.setValueAsString(myFullUrl); 328 fullUrlChild.getMutator().setValue(entry, fullUrlValue); 329 } 330 } 331 332 // Set resource 333 if (myResource != null) { 334 BaseRuntimeChildDefinition resourceChild = entryDef.getChildByName("resource"); 335 if (resourceChild != null) { 336 resourceChild.getMutator().setValue(entry, myResource); 337 } 338 } 339 340 // Set request fields 341 if (myRequestMethod != null 342 || myRequestUrl != null 343 || myRequestIfNoneMatch != null 344 || myRequestIfModifiedSince != null 345 || myRequestIfMatch != null 346 || myRequestIfNoneExist != null) { 347 348 BaseRuntimeChildDefinition requestChild = entryDef.getChildByName("request"); 349 if (requestChild != null) { 350 IBase request = requestChild.getChildByName("request").newInstance(); 351 requestChild.getMutator().setValue(entry, request); 352 353 BaseRuntimeElementCompositeDefinition<?> requestDef = 354 (BaseRuntimeElementCompositeDefinition<?>) requestChild.getChildByName("request"); 355 356 if (myRequestMethod != null) { 357 BaseRuntimeChildDefinition methodChild = requestDef.getChildByName("method"); 358 if (methodChild != null) { 359 IPrimitiveType<?> methodValue = (IPrimitiveType<?>) methodChild 360 .getChildByName("method") 361 .newInstance(methodChild.getInstanceConstructorArguments()); 362 methodValue.setValueAsString(myRequestMethod); 363 methodChild.getMutator().setValue(request, methodValue); 364 } 365 } 366 367 if (myRequestUrl != null) { 368 BaseRuntimeChildDefinition urlChild = requestDef.getChildByName("url"); 369 if (urlChild != null) { 370 IPrimitiveType<?> urlValue = (IPrimitiveType<?>) 371 urlChild.getChildByName("url").newInstance(); 372 urlValue.setValueAsString(myRequestUrl); 373 urlChild.getMutator().setValue(request, urlValue); 374 } 375 } 376 377 if (myRequestIfNoneMatch != null) { 378 BaseRuntimeChildDefinition ifNoneMatchChild = requestDef.getChildByName("ifNoneMatch"); 379 if (ifNoneMatchChild != null) { 380 IPrimitiveType<?> ifNoneMatchValue = (IPrimitiveType<?>) 381 ifNoneMatchChild.getChildByName("ifNoneMatch").newInstance(); 382 ifNoneMatchValue.setValueAsString(myRequestIfNoneMatch); 383 ifNoneMatchChild.getMutator().setValue(request, ifNoneMatchValue); 384 } 385 } 386 387 if (myRequestIfModifiedSince != null) { 388 BaseRuntimeChildDefinition ifModifiedSinceChild = requestDef.getChildByName("ifModifiedSince"); 389 if (ifModifiedSinceChild != null) { 390 IPrimitiveType<?> ifModifiedSinceValue = (IPrimitiveType<?>) ifModifiedSinceChild 391 .getChildByName("ifModifiedSince") 392 .newInstance(); 393 ifModifiedSinceValue.setValueAsString(myRequestIfModifiedSince); 394 ifModifiedSinceChild.getMutator().setValue(request, ifModifiedSinceValue); 395 } 396 } 397 398 if (myRequestIfMatch != null) { 399 BaseRuntimeChildDefinition ifMatchChild = requestDef.getChildByName("ifMatch"); 400 if (ifMatchChild != null) { 401 IPrimitiveType<?> ifMatchValue = (IPrimitiveType<?>) 402 ifMatchChild.getChildByName("ifMatch").newInstance(); 403 ifMatchValue.setValueAsString(myRequestIfMatch); 404 ifMatchChild.getMutator().setValue(request, ifMatchValue); 405 } 406 } 407 408 if (myRequestIfNoneExist != null) { 409 BaseRuntimeChildDefinition ifNoneExistChild = requestDef.getChildByName("ifNoneExist"); 410 if (ifNoneExistChild != null) { 411 IPrimitiveType<?> ifNoneExistValue = (IPrimitiveType<?>) 412 ifNoneExistChild.getChildByName("ifNoneExist").newInstance(); 413 ifNoneExistValue.setValueAsString(myRequestIfNoneExist); 414 ifNoneExistChild.getMutator().setValue(request, ifNoneExistValue); 415 } 416 } 417 } 418 } 419 420 // Set response fields 421 if (myResponseStatus != null 422 || myResponseLocation != null 423 || myResponseEtag != null 424 || myResponseLastModified != null) { 425 BaseRuntimeChildDefinition responseChild = entryDef.getChildByName("response"); 426 if (responseChild != null) { 427 IBase response = responseChild.getChildByName("response").newInstance(); 428 responseChild.getMutator().setValue(entry, response); 429 430 BaseRuntimeElementCompositeDefinition<?> responseDef = 431 (BaseRuntimeElementCompositeDefinition<?>) responseChild.getChildByName("response"); 432 433 if (myResponseStatus != null) { 434 BaseRuntimeChildDefinition statusChild = responseDef.getChildByName("status"); 435 if (statusChild != null) { 436 IPrimitiveType<?> statusValue = (IPrimitiveType<?>) 437 statusChild.getChildByName("status").newInstance(); 438 statusValue.setValueAsString(myResponseStatus); 439 statusChild.getMutator().setValue(response, statusValue); 440 } 441 } 442 443 if (myResponseLocation != null) { 444 BaseRuntimeChildDefinition locationChild = responseDef.getChildByName("location"); 445 if (locationChild != null) { 446 IPrimitiveType<?> locationValue = (IPrimitiveType<?>) 447 locationChild.getChildByName("location").newInstance(); 448 locationValue.setValueAsString(myResponseLocation); 449 locationChild.getMutator().setValue(response, locationValue); 450 } 451 } 452 453 if (myResponseEtag != null) { 454 BaseRuntimeChildDefinition etagChild = responseDef.getChildByName("etag"); 455 if (etagChild != null) { 456 IPrimitiveType<?> etagValue = (IPrimitiveType<?>) 457 etagChild.getChildByName("etag").newInstance(); 458 etagValue.setValueAsString(myResponseEtag); 459 etagChild.getMutator().setValue(response, etagValue); 460 } 461 } 462 463 if (myResponseLastModified != null) { 464 BaseRuntimeChildDefinition lastModifiedChild = responseDef.getChildByName("lastModified"); 465 if (lastModifiedChild != null) { 466 IPrimitiveType<?> lastModifiedValue = (IPrimitiveType<?>) 467 lastModifiedChild.getChildByName("lastModified").newInstance(); 468 lastModifiedValue.setValueAsString(myResponseLastModified); 469 lastModifiedChild.getMutator().setValue(response, lastModifiedValue); 470 } 471 } 472 473 if (myResponseOutcome != null) { 474 BaseRuntimeChildDefinition outcomeChild = responseDef.getChildByName("outcome"); 475 if (outcomeChild != null) { 476 outcomeChild.getMutator().setValue(response, myResponseOutcome); 477 } 478 } 479 } 480 } 481 482 // Set search fields 483 if (mySearchMode != null || mySearchScore != null) { 484 BaseRuntimeChildDefinition searchChild = entryDef.getChildByName("search"); 485 if (searchChild != null) { 486 IBase search = searchChild.getChildByName("search").newInstance(); 487 searchChild.getMutator().setValue(entry, search); 488 489 BaseRuntimeElementCompositeDefinition<?> searchDef = 490 (BaseRuntimeElementCompositeDefinition<?>) searchChild.getChildByName("search"); 491 492 if (mySearchMode != null) { 493 BaseRuntimeChildDefinition modeChild = searchDef.getChildByName("mode"); 494 if (modeChild != null) { 495 IPrimitiveType<?> modeValue = (IPrimitiveType<?>) 496 modeChild.getChildByName("mode").newInstance(); 497 modeValue.setValueAsString(mySearchMode); 498 modeChild.getMutator().setValue(search, modeValue); 499 } 500 } 501 502 if (mySearchScore != null) { 503 BaseRuntimeChildDefinition scoreChild = searchDef.getChildByName("score"); 504 if (scoreChild != null) { 505 IPrimitiveType<?> scoreValue = (IPrimitiveType<?>) 506 scoreChild.getChildByName("score").newInstance(); 507 scoreValue.setValueAsString(mySearchScore); 508 scoreChild.getMutator().setValue(search, scoreValue); 509 } 510 } 511 } 512 } 513 514 return entry; 515 } 516}