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