
001package org.hl7.fhir.r5.context; 002 003/* 004 Copyright (c) 2011+, HL7, Inc. 005 All rights reserved. 006 007 Redistribution and use in source and binary forms, with or without modification, 008 are permitted provided that the following conditions are met: 009 010 * Redistributions of source code must retain the above copyright notice, this 011 list of conditions and the following disclaimer. 012 * Redistributions in binary form must reproduce the above copyright notice, 013 this list of conditions and the following disclaimer in the documentation 014 and/or other materials provided with the distribution. 015 * Neither the name of HL7 nor the names of its contributors may be used to 016 endorse or promote products derived from this software without specific 017 prior written permission. 018 019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 022 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 023 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 024 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 025 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 026 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 027 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 028 POSSIBILITY OF SUCH DAMAGE. 029 030 */ 031 032import java.io.ByteArrayInputStream; 033import java.io.FileInputStream; 034import java.io.FileNotFoundException; 035import java.io.IOException; 036import java.io.InputStream; 037import java.util.ArrayList; 038import java.util.HashSet; 039import java.util.List; 040import java.util.Locale; 041import java.util.Map; 042import java.util.Set; 043import java.util.UUID; 044import java.util.zip.ZipEntry; 045import java.util.zip.ZipInputStream; 046 047import lombok.AccessLevel; 048import lombok.AllArgsConstructor; 049import lombok.With; 050import org.apache.commons.io.IOUtils; 051import org.hl7.fhir.exceptions.DefinitionException; 052import org.hl7.fhir.exceptions.FHIRException; 053import org.hl7.fhir.exceptions.FHIRFormatError; 054import org.hl7.fhir.exceptions.TerminologyServiceException; 055import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy; 056import org.hl7.fhir.r5.context.ILoggingService.LogCategory; 057import org.hl7.fhir.r5.formats.IParser; 058import org.hl7.fhir.r5.formats.JsonParser; 059import org.hl7.fhir.r5.formats.XmlParser; 060import org.hl7.fhir.r5.model.*; 061import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent; 062import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; 063import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; 064import org.hl7.fhir.r5.model.StructureMap.StructureMapModelMode; 065import org.hl7.fhir.r5.model.StructureMap.StructureMapStructureComponent; 066import org.hl7.fhir.r5.terminologies.JurisdictionUtilities; 067import org.hl7.fhir.r5.terminologies.client.ITerminologyClient; 068import org.hl7.fhir.r5.terminologies.client.TerminologyClientContext; 069import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.ITerminologyClientFactory; 070import org.hl7.fhir.r5.terminologies.client.TerminologyClientR5; 071import org.hl7.fhir.r5.utils.validation.IResourceValidator; 072import org.hl7.fhir.r5.utils.validation.ValidatorSession; 073import org.hl7.fhir.r5.utils.R5Hacker; 074import org.hl7.fhir.r5.utils.UserDataNames; 075import org.hl7.fhir.r5.utils.XVerExtensionManager; 076import org.hl7.fhir.utilities.ByteProvider; 077import org.hl7.fhir.utilities.MagicResources; 078import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 079import org.hl7.fhir.utilities.FileUtilities; 080import org.hl7.fhir.utilities.TimeTracker; 081import org.hl7.fhir.utilities.Utilities; 082import org.hl7.fhir.utilities.VersionUtilities; 083import org.hl7.fhir.utilities.filesystem.CSFileInputStream; 084import org.hl7.fhir.utilities.filesystem.ManagedFileAccess; 085import org.hl7.fhir.utilities.i18n.I18nConstants; 086import org.hl7.fhir.utilities.npm.BasePackageCacheManager; 087import org.hl7.fhir.utilities.npm.NpmPackage; 088import org.hl7.fhir.utilities.npm.NpmPackage.PackageResourceInformation; 089 090import ca.uhn.fhir.parser.DataFormatException; 091 092/* 093 * This is a stand alone implementation of worker context for use inside a tool. 094 * It loads from the validation package (validation-min.xml.zip), and has a 095 * very light client to connect to an open unauthenticated terminology service 096 */ 097 098@MarkedToMoveToAdjunctPackage 099public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerContext { 100 101 public static class PackageResourceLoader extends CanonicalResourceProxy { 102 103 private final String filename; 104 private final IContextResourceLoader loader; 105 private final PackageInformation packageInformation; 106 107 public PackageResourceLoader(PackageResourceInformation pri, IContextResourceLoader loader, PackageInformation pi) { 108 super(pri.getResourceType(), pri.getId(), loader == null ? pri.getUrl() :loader.patchUrl(pri.getUrl(), pri.getResourceType()), pri.getVersion(), pri.getSupplements(), pri.getDerivation(), pri.getContent()); 109 this.filename = pri.getFilename(); 110 this.loader = loader; 111 this.packageInformation = pi; 112 } 113 114 @Override 115 public CanonicalResource loadResource() { 116 try { 117 FileInputStream f = ManagedFileAccess.inStream(filename); 118 try { 119 if (loader != null) { 120 return setPi(R5Hacker.fixR5BrokenResource((CanonicalResource) loader.loadResource(f, true))); 121 } else { 122 return setPi(R5Hacker.fixR5BrokenResource((CanonicalResource) new JsonParser().parse(f))); 123 } 124 } finally { 125 f.close(); 126 } 127 } catch (Exception e) { 128 throw new FHIRException("Error loading "+filename+": "+e.getMessage(), e); 129 } 130 } 131 132 private CanonicalResource setPi(CanonicalResource cr) { 133 cr.setSourcePackage(packageInformation); 134 return cr; 135 } 136 } 137 138 public interface ILoadFilter { 139 boolean isOkToLoad(Resource resource); 140 boolean isOkToLoad(String resourceType); 141 } 142 143 public interface IValidatorFactory { 144 IResourceValidator makeValidator(IWorkerContext ctxt, ValidatorSession session) throws FHIRException; 145 IResourceValidator makeValidator(IWorkerContext ctxts, XVerExtensionManager xverManager, ValidatorSession session) throws FHIRException; 146 } 147 148 private Questionnaire questionnaire; 149 private String revision; 150 private String date; 151 private IValidatorFactory validatorFactory; 152 private boolean progress; 153 private final List<String> loadedPackages = new ArrayList<>(); 154 private boolean canNoTS; 155 private XVerExtensionManager xverManager; 156 private boolean allowLazyLoading = true; 157 158 private SimpleWorkerContext() throws IOException, FHIRException { 159 super(); 160 } 161 162 private SimpleWorkerContext(Locale locale) throws IOException, FHIRException { 163 super(locale); 164 } 165 166 public SimpleWorkerContext(SimpleWorkerContext other) throws IOException, FHIRException { 167 super(); 168 copy(other); 169 } 170 171 private SimpleWorkerContext(SimpleWorkerContext other, Locale locale) throws IOException, FHIRException { 172 super(locale); 173 copy(other); 174 } 175 176 protected void copy(SimpleWorkerContext other) { 177 super.copy(other); 178 binaries.putAll(other.binaries); 179 version = other.version; 180 revision = other.revision; 181 date = other.date; 182 validatorFactory = other.validatorFactory; 183 progress = other.progress; 184 loadedPackages.addAll(other.loadedPackages); 185 canNoTS = other.canNoTS; 186 xverManager = other.xverManager; 187 allowLazyLoading = other.allowLazyLoading; 188 } 189 190 191 public List<String> getLoadedPackages() { 192 return loadedPackages; 193 } 194 195 // -- Initializations 196 @AllArgsConstructor(access = AccessLevel.PRIVATE) 197 public static class SimpleWorkerContextBuilder { 198 199 200 @With 201 private final String terminologyCachePath; 202 @With 203 private final boolean cacheTerminologyClientErrors; 204 @With 205 private final boolean alwaysUseTerminologyServer; 206 @With 207 private final boolean readOnlyCache; 208 209 @With 210 private final Locale locale; 211 212 @With 213 private final String userAgent; 214 215 @With 216 private final boolean allowLoadingDuplicates; 217 218 @With 219 private final org.hl7.fhir.r5.context.ILoggingService loggingService; 220 private boolean defaultExpParams; 221 222 public SimpleWorkerContextBuilder() { 223 cacheTerminologyClientErrors = false; 224 alwaysUseTerminologyServer = false; 225 readOnlyCache = false; 226 terminologyCachePath = null; 227 locale = null; 228 userAgent = null; 229 allowLoadingDuplicates = false; 230 loggingService = new SystemOutLoggingService(); 231 } 232 233 private SimpleWorkerContext getSimpleWorkerContextInstance() throws IOException { 234 if (locale != null) { 235 return new SimpleWorkerContext(locale); 236 } else { 237 return new SimpleWorkerContext(); 238 } 239 } 240 241 public SimpleWorkerContext build() throws IOException { 242 SimpleWorkerContext context = getSimpleWorkerContextInstance(); 243 return build(context); 244 } 245 246 private SimpleWorkerContext build(SimpleWorkerContext context) throws IOException { 247 if (VersionUtilities.isR2Ver(context.getVersion()) || VersionUtilities.isR2Ver(context.getVersion())) { 248 System.out.println("As of end 2024, FHIR R2 (version "+context.getVersion()+") is no longer officially supported."); 249 } 250 context.initTxCache(terminologyCachePath); 251 context.setUserAgent(userAgent); 252 context.setLogger(loggingService); 253 context.cacheResource(new org.hl7.fhir.r5.formats.JsonParser().parse(MagicResources.spdxCodesAsData())); 254 if (defaultExpParams) { 255 context.setExpansionParameters(makeExpProfile()); 256 } 257 return context; 258 } 259 260 public SimpleWorkerContext fromPackage(NpmPackage pi) throws IOException, FHIRException { 261 SimpleWorkerContext context = getSimpleWorkerContextInstance(); 262 context.setAllowLoadingDuplicates(allowLoadingDuplicates); 263 context.terminologyClientManager.setFactory(TerminologyClientR5.factory()); 264 context.loadFromPackage(pi, null); 265 return build(context); 266 } 267 268 private Parameters makeExpProfile() { 269 Parameters ep = new Parameters(); 270 ep.addParameter("cache-id", UUID.randomUUID().toString().toLowerCase()); 271 return ep; 272 } 273 274 public SimpleWorkerContext fromPackage(NpmPackage pi, IContextResourceLoader loader, boolean genSnapshots) throws IOException, FHIRException { 275 SimpleWorkerContext context = getSimpleWorkerContextInstance(); 276 context.setAllowLoadingDuplicates(allowLoadingDuplicates); 277 context.version = pi.fhirVersion(); 278 context.terminologyClientManager.setFactory(loader.txFactory()); 279 context.loadFromPackage(pi, loader); 280 context.finishLoading(genSnapshots); 281 if (defaultExpParams) { 282 context.setExpansionParameters(makeExpProfile()); 283 } 284 return build(context); 285 } 286 287 /** 288 * Load the working context from the validation pack 289 * 290 * @param path 291 * filename of the validation pack 292 * @return 293 * @throws IOException 294 * @throws FileNotFoundException 295 * @throws FHIRException 296 * @throws Exception 297 */ 298 public SimpleWorkerContext fromPack(String path) throws IOException, FHIRException { 299 SimpleWorkerContext context = getSimpleWorkerContextInstance(); 300 context.setAllowLoadingDuplicates(allowLoadingDuplicates); 301 context.loadFromPack(path, null); 302 return build(context); 303 } 304 305 public SimpleWorkerContext fromPack(String path, IContextResourceLoader loader) throws IOException, FHIRException { 306 SimpleWorkerContext context = getSimpleWorkerContextInstance(); 307 context.loadFromPack(path, loader); 308 return build(context); 309 } 310 311 public SimpleWorkerContext fromClassPath() throws IOException, FHIRException { 312 SimpleWorkerContext context = getSimpleWorkerContextInstance(); 313 context.loadFromStream(SimpleWorkerContext.class.getResourceAsStream("validation.json.zip"), null); 314 return build(context); 315 } 316 317 public SimpleWorkerContext fromClassPath(String name) throws IOException, FHIRException { 318 SimpleWorkerContext context = getSimpleWorkerContextInstance(); 319 InputStream s = SimpleWorkerContext.class.getResourceAsStream("/" + name); 320 context.setAllowLoadingDuplicates(allowLoadingDuplicates); 321 context.loadFromStream(s, null); 322 return build(context); 323 } 324 325 public SimpleWorkerContext fromDefinitions(Map<String, ByteProvider> source, IContextResourceLoader loader, PackageInformation pi) throws IOException, FHIRException { 326 SimpleWorkerContext context = getSimpleWorkerContextInstance(); 327 for (String name : source.keySet()) { 328 try { 329 context.loadDefinitionItem(name, new ByteArrayInputStream(source.get(name).getBytes()), loader, null, pi); 330 } catch (Exception e) { 331 System.out.println("Error loading "+name+": "+e.getMessage()); 332 throw new FHIRException("Error loading "+name+": "+e.getMessage(), e); 333 } 334 } 335 return build(context); 336 } 337 public SimpleWorkerContext fromNothing() throws FHIRException, IOException { 338 return build(); 339 } 340 341 public SimpleWorkerContextBuilder withDefaultParams() { 342 defaultExpParams = true; 343 return this; 344 } 345 } 346 347 private void loadDefinitionItem(String name, InputStream stream, IContextResourceLoader loader, ILoadFilter filter, PackageInformation pi) throws IOException, FHIRException { 348 if (name.endsWith(".xml")) 349 loadFromFile(stream, name, loader, filter); 350 else if (name.endsWith(".json")) 351 loadFromFileJson(stream, name, loader, filter, pi); 352 else if (name.equals("version.info")) 353 readVersionInfo(stream); 354 else 355 binaries.put(name, new BytesProvider(FileUtilities.streamToBytesNoClose(stream))); 356 } 357 358 public void connectToTSServer(ITerminologyClientFactory factory, ITerminologyClient client, boolean useEcosystem) { 359 terminologyClientManager.setFactory(factory); 360 if (txLog == null) { 361 txLog = client.getLogger(); 362 } 363 TerminologyClientContext tcc = terminologyClientManager.setMasterClient(client, useEcosystem); 364 txLog("Connect to "+client.getAddress()); 365 try { 366 tcc.initialize(); 367 } catch (Exception e) { 368 if (canRunWithoutTerminology) { 369 noTerminologyServer = true; 370 logger.logMessage("==============!! Running without terminology server !! =============="); 371 if (terminologyClientManager.getMasterClient() != null) { 372 logger.logMessage("txServer = "+ terminologyClientManager.getMasterClient().getId()); 373 logger.logMessage("Error = "+e.getMessage()+""); 374 } 375 logger.logMessage("====================================================================="); 376 } else { 377 e.printStackTrace(); 378 throw new TerminologyServiceException(e); 379 } 380 } 381 } 382 383 public void connectToTSServer(ITerminologyClientFactory factory, String address, String software, String log, boolean useEcosystem) { 384 try { 385 terminologyClientManager.setFactory(factory); 386 if (log != null && (log.endsWith(".htm") || log.endsWith(".html"))) { 387 txLog = new HTMLClientLogger(log); 388 } else { 389 txLog = new TextClientLogger(log); 390 } 391 ITerminologyClient client = factory.makeClient("tx-server", address, software, txLog); 392 // txFactory.makeClient("Tx-Server", txServer, "fhir/publisher", null) 393// terminologyClientManager.setLogger(txLog); 394// terminologyClientManager.setUserAgent(userAgent); 395 connectToTSServer(factory, client, useEcosystem); 396 397 } catch (Exception e) { 398 e.printStackTrace(); 399 throw new FHIRException(formatMessage(canNoTS ? I18nConstants.UNABLE_TO_CONNECT_TO_TERMINOLOGY_SERVER_USE_PARAMETER_TX_NA_TUN_RUN_WITHOUT_USING_TERMINOLOGY_SERVICES_TO_VALIDATE_LOINC_SNOMED_ICDX_ETC_ERROR__ : I18nConstants.UNABLE_TO_CONNECT_TO_TERMINOLOGY_SERVER, e.getMessage(), address), e); 400 } 401 } 402 403 public void loadFromFile(InputStream stream, String name, IContextResourceLoader loader) throws FHIRException { 404 loadFromFile(stream, name, loader, null); 405 } 406 407 public void loadFromFile(InputStream stream, String name, IContextResourceLoader loader, ILoadFilter filter) throws FHIRException { 408 Resource f; 409 try { 410 if (loader != null) 411 f = loader.loadBundle(stream, false); 412 else { 413 XmlParser xml = new XmlParser(); 414 f = xml.parse(stream); 415 } 416 } catch (DataFormatException e1) { 417 throw new org.hl7.fhir.exceptions.FHIRFormatError(formatMessage(I18nConstants.ERROR_PARSING_, name, e1.getMessage()), e1); 418 } catch (Exception e1) { 419 throw new org.hl7.fhir.exceptions.FHIRFormatError(formatMessage(I18nConstants.ERROR_PARSING_, name, e1.getMessage()), e1); 420 } 421 if (f instanceof Bundle) { 422 Bundle bnd = (Bundle) f; 423 for (BundleEntryComponent e : bnd.getEntry()) { 424 if (e.getFullUrl() == null) { 425 logger.logDebugMessage(LogCategory.CONTEXT, "unidentified resource in " + name+" (no fullUrl)"); 426 } 427 if (filter == null || filter.isOkToLoad(e.getResource())) { 428 String path = loader != null ? loader.getResourcePath(e.getResource()) : null; 429 if (path != null) { 430 e.getResource().setWebPath(path); 431 } 432 cacheResource(e.getResource()); 433 } 434 } 435 } else if (f instanceof CanonicalResource) { 436 if (filter == null || filter.isOkToLoad(f)) { 437 String path = loader != null ? loader.getResourcePath(f) : null; 438 if (path != null) { 439 f.setWebPath(path); 440 } 441 cacheResource(f); 442 } 443 } 444 } 445 446 private void loadFromFileJson(InputStream stream, String name, IContextResourceLoader loader, ILoadFilter filter, PackageInformation pi) throws IOException, FHIRException { 447 Bundle f = null; 448 try { 449 if (loader != null) 450 f = loader.loadBundle(stream, true); 451 else { 452 JsonParser json = new JsonParser(); 453 Resource r = json.parse(stream); 454 if (r instanceof Bundle) 455 f = (Bundle) r; 456 else if (filter == null || filter.isOkToLoad(f)) { 457 cacheResourceFromPackage(r, pi); 458 } 459 } 460 } catch (FHIRFormatError e1) { 461 throw new org.hl7.fhir.exceptions.FHIRFormatError(e1.getMessage(), e1); 462 } 463 if (f != null) 464 for (BundleEntryComponent e : f.getEntry()) { 465 if (filter == null || filter.isOkToLoad(e.getResource())) { 466 String path = loader != null ? loader.getResourcePath(e.getResource()) : null; 467 if (path != null) { 468 e.getResource().setWebPath(path); 469 } 470 cacheResourceFromPackage(e.getResource(), pi); 471 } 472 } 473 } 474 475 private void loadFromPack(String path, IContextResourceLoader loader) throws IOException, FHIRException { 476 loadFromStream(new CSFileInputStream(path), loader); 477 } 478 479 480 @Override 481 public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader) throws IOException, FHIRException { 482 return loadFromPackageInt(pi, loader, loader == null ? defaultTypesToLoad() : loader.getTypes()); 483 } 484 485 public static List<String> defaultTypesToLoad() { 486 // there's no penalty for listing resources that don't exist, so we just all the relevant possibilities for all versions 487 return Utilities.strings("CodeSystem", "ValueSet", "ConceptMap", "NamingSystem", 488 "StructureDefinition", "StructureMap", 489 "SearchParameter", "OperationDefinition", "CapabilityStatement", "Conformance", 490 "Questionnaire", "ImplementationGuide", "Measure" ); 491 } 492 493 @Override 494 public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader, List<String> types) throws IOException, FHIRException { 495 return loadFromPackageInt(pi, loader, types); 496 } 497 498 @Override 499 public int loadFromPackageAndDependencies(NpmPackage pi, IContextResourceLoader loader, BasePackageCacheManager pcm) throws IOException, FHIRException { 500 return loadFromPackageAndDependenciesInt(pi, loader, pcm, pi.name()+"#"+pi.version()); 501 } 502 public int loadFromPackageAndDependenciesInt(NpmPackage pi, IContextResourceLoader loader, BasePackageCacheManager pcm, String path) throws IOException, FHIRException { 503 int t = 0; 504 505 for (String e : pi.dependencies()) { 506 if (!loadedPackages.contains(e) && !VersionUtilities.isCorePackage(e)) { 507 NpmPackage npm = pcm.loadPackage(e); 508 if (!VersionUtilities.versionsMatch(version, npm.fhirVersion())) { 509 System.out.println(formatMessage(I18nConstants.PACKAGE_VERSION_MISMATCH, e, version, npm.fhirVersion(), path)); 510 } 511 t = t + loadFromPackageAndDependenciesInt(npm, loader.getNewLoader(npm), pcm, path+" -> "+npm.name()+"#"+npm.version()); 512 } 513 } 514 t = t + loadFromPackageInt(pi, loader, loader.getTypes()); 515 return t; 516 } 517 518 519 public int loadFromPackageInt(NpmPackage pi, IContextResourceLoader loader, List<String> types) throws IOException, FHIRException { 520 int t = 0; 521 if (progress) { 522 System.out.println("Load Package "+pi.name()+"#"+pi.version()); 523 } 524 if (loadedPackages.contains(pi.id()+"#"+pi.version())) { 525 return 0; 526 } 527 528 loadedPackages.add(pi.id()+"#"+pi.version()); 529 if (packageTracker != null) { 530 packageTracker.packageLoaded(pi.id(), pi.version()); 531 } 532 533 String of = pi.getFolders().get("package").getFolderPath(); 534 if (of != null) { 535 oidSources.add(new OIDSource(of, pi.vid())); 536 } 537 538 if ((types == null || types.size() == 0) && loader != null) { 539 types = loader.getTypes(); 540 } 541 PackageInformation pii = new PackageInformation(pi); 542 if (VersionUtilities.isR2Ver(pi.fhirVersion()) || !pi.canLazyLoad() || !allowLazyLoading) { 543 // can't lazy load R2 because of valueset/codesystem implementation 544 if (types == null || types.size() == 0) { 545 types = Utilities.strings("StructureDefinition", "ValueSet", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem" ); 546 } 547 for (String s : pi.listResources(types)) { 548 try { 549 loadDefinitionItem(s, pi.load("package", s), loader, null, pii); 550 t++; 551 } catch (Exception e) { 552 throw new FHIRException(formatMessage(I18nConstants.ERROR_READING__FROM_PACKAGE__, s, pi.name(), pi.version(), e.getMessage()), e); 553 } 554 } 555 } else { 556 if (types == null || types.size() == 0) { 557 types = Utilities.strings("StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem", "Measures" ); 558 } 559 for (PackageResourceInformation pri : pi.listIndexedResources(types)) { 560 if (!pri.getFilename().contains("ig-r4") && (loader == null || loader.wantLoad(pi, pri))) { 561 try { 562 if (!pri.hasId()) { 563 loadDefinitionItem(pri.getFilename(), ManagedFileAccess.inStream(pri.getFilename()), loader, null, pii); 564 } else { 565 registerResourceFromPackage(new PackageResourceLoader(pri, loader, pii), pii); 566 } 567 t++; 568 } catch (FHIRException e) { 569 throw new FHIRException(formatMessage(I18nConstants.ERROR_READING__FROM_PACKAGE__, pri.getFilename(), pi.name(), pi.version(), e.getMessage()), e); 570 } 571 } 572 } 573 } 574 for (String s : pi.list("other")) { 575 binaries.put(s, new BytesFromPackageProvider(pi, s)); 576 } 577 if (version == null) { 578 version = pi.version(); 579 if (version.equals("current")) { 580 version = "5.0.0"; 581 } 582 } 583 if (loader != null && terminologyClientManager.getFactory() == null) { 584 terminologyClientManager.setFactory(loader.txFactory()); 585 } 586 return t; 587 } 588 589 public void loadFromFile(String file, IContextResourceLoader loader) throws IOException, FHIRException { 590 loadDefinitionItem(file, new CSFileInputStream(file), loader, null, null); 591 } 592 593 private void loadFromStream(InputStream stream, IContextResourceLoader loader) throws IOException, FHIRException { 594 ZipInputStream zip = new ZipInputStream(stream); 595 ZipEntry zipEntry; 596 while ((zipEntry = zip.getNextEntry()) != null) { 597 String entryName = zipEntry.getName(); 598 if (entryName.contains("..")) { 599 throw new RuntimeException("Entry with an illegal path: " + entryName); 600 } 601 loadDefinitionItem(entryName, zip, loader, null, null); 602 zip.closeEntry(); 603 } 604 zip.close(); 605 } 606 607 private void readVersionInfo(InputStream stream) throws IOException, DefinitionException { 608 byte[] bytes = IOUtils.toByteArray(stream); 609 binaries.put("version.info", new BytesProvider(bytes)); 610 611 String[] vi = new String(bytes).split("\\r?\\n"); 612 for (String s : vi) { 613 if (s.startsWith("version=")) { 614 if (version == null) 615 version = s.substring(8); 616 else if (!version.equals(s.substring(8))) { 617 throw new DefinitionException(formatMessage(I18nConstants.VERSION_MISMATCH_THE_CONTEXT_HAS_VERSION__LOADED_AND_THE_NEW_CONTENT_BEING_LOADED_IS_VERSION_, version, s.substring(8))); 618 } 619 } 620 if (s.startsWith("revision=")) 621 revision = s.substring(9); 622 if (s.startsWith("date=")) 623 date = s.substring(5); 624 } 625 } 626 627 @Override 628 public IResourceValidator newValidator() throws FHIRException { 629 if (validatorFactory == null) 630 throw new Error(formatMessage(I18nConstants.NO_VALIDATOR_CONFIGURED)); 631 return validatorFactory.makeValidator(this, xverManager, null).setJurisdiction(JurisdictionUtilities.getJurisdictionCodingFromLocale(Locale.getDefault().getCountry())); 632 } 633 634 @Override 635 public List<String> getResourceNames() { 636 Set<String> result = new HashSet<String>(); 637 for (StructureDefinition sd : listStructures()) { 638 if (sd.getKind() == StructureDefinitionKind.RESOURCE && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION && !sd.hasUserData(UserDataNames.loader_urls_patched)) 639 result.add(sd.getName()); 640 } 641 return Utilities.sorted(result); 642 } 643 644 645 public Questionnaire getQuestionnaire() { 646 return questionnaire; 647 } 648 649 public void setQuestionnaire(Questionnaire questionnaire) { 650 this.questionnaire = questionnaire; 651 } 652 653 654 655 public void loadBinariesFromFolder(String folder) throws IOException { 656 for (String n : ManagedFileAccess.file(folder).list()) { 657 binaries.put(n, new BytesFromFileProvider(Utilities.path(folder, n))); 658 } 659 } 660 661 public void loadBinariesFromFolder(NpmPackage pi) throws IOException { 662 for (String n : pi.list("other")) { 663 binaries.put(n, new BytesFromPackageProvider(pi, n)); 664 } 665 } 666 667 public void loadFromFolder(String folder) throws IOException { 668 for (String n : ManagedFileAccess.file(folder).list()) { 669 if (n.endsWith(".json")) 670 loadFromFile(Utilities.path(folder, n), new JsonParser()); 671 else if (n.endsWith(".xml")) 672 loadFromFile(Utilities.path(folder, n), new XmlParser()); 673 } 674 } 675 676 private void loadFromFile(String filename, IParser p) { 677 Resource r; 678 try { 679 r = p.parse(ManagedFileAccess.inStream(filename)); 680 if (r.getResourceType() == ResourceType.Bundle) { 681 for (BundleEntryComponent e : ((Bundle) r).getEntry()) { 682 cacheResource(e.getResource()); 683 } 684 } else { 685 cacheResource(r); 686 } 687 } catch (Exception e) { 688 return; 689 } 690 } 691 692 693 694 @Override 695 public String getVersion() { 696 return version; 697 } 698 699 700 public List<StructureMap> findTransformsforSource(String url) { 701 List<StructureMap> res = new ArrayList<StructureMap>(); 702 for (StructureMap map : fetchResourcesByType(StructureMap.class)) { 703 boolean match = false; 704 boolean ok = true; 705 for (StructureMapStructureComponent t : map.getStructure()) { 706 if (t.getMode() == StructureMapModelMode.SOURCE) { 707 match = match || t.getUrl().equals(url); 708 ok = ok && t.getUrl().equals(url); 709 } 710 } 711 if (match && ok) 712 res.add(map); 713 } 714 return res; 715 } 716 717 public IValidatorFactory getValidatorFactory() { 718 return validatorFactory; 719 } 720 721 public void setValidatorFactory(IValidatorFactory validatorFactory) { 722 this.validatorFactory = validatorFactory; 723 } 724 725 @Override 726 public <T extends Resource> T fetchResource(Class<T> class_, String uri) { 727 T r = super.fetchResource(class_, uri); 728 if (r instanceof StructureDefinition) { 729 StructureDefinition p = (StructureDefinition)r; 730 try { 731 new ContextUtilities(this).generateSnapshot(p); 732 } catch (Exception e) { 733 // not sure what to do in this case? 734 System.out.println("Unable to generate snapshot @3 for "+uri+": "+e.getMessage()); 735 if (logger.isDebugLogging()) { 736 e.printStackTrace(); 737 } 738 } 739 } 740 return r; 741 } 742 743 @Override 744 public <T extends Resource> T fetchResourceRaw(Class<T> class_, String uri) { 745 T r = super.fetchResource(class_, uri); 746 return r; 747 } 748 749 @Override 750 public <T extends Resource> T fetchResource(Class<T> class_, String uri, Resource source) { 751 T resource = super.fetchResource(class_, uri, source); 752 if (resource instanceof StructureDefinition) { 753 StructureDefinition structureDefinition = (StructureDefinition)resource; 754 generateSnapshot(structureDefinition, "4"); 755 } 756 return resource; 757 } 758 759 760 761 762 public String listMapUrls() { 763 return Utilities.listCanonicalUrls(transforms.keys()); 764 } 765 766 public boolean isProgress() { 767 return progress; 768 } 769 770 public void setProgress(boolean progress) { 771 this.progress = progress; 772 } 773 774 public void setClock(TimeTracker tt) { 775 clock = tt; 776 } 777 778 public boolean isCanNoTS() { 779 return canNoTS; 780 } 781 782 public void setCanNoTS(boolean canNoTS) { 783 this.canNoTS = canNoTS; 784 } 785 786 public XVerExtensionManager getXVer() { 787 if (xverManager == null) { 788 xverManager = new XVerExtensionManager(this); 789 } 790 return xverManager; 791 } 792 793 public void cachePackage(PackageInformation packageInfo) { 794 // nothing yet 795 } 796 797 @Override 798 public boolean hasPackage(String id, String ver) { 799 return loadedPackages.contains(id+"#"+ver); 800 } 801 802 public boolean hasPackage(String idAndver) { 803 if (loadedPackages.contains(idAndver)) { 804 return true; 805 } 806 // not clear whether the same logic should apply to other cross-version packages? 807 if (idAndver.startsWith("hl7.fhir.uv.extensions")) { 808 String v = idAndver.substring(idAndver.lastIndexOf("#")+1); 809 for (String s : loadedPackages) { 810 String v2 = s.substring(s.lastIndexOf("#")+1); 811 if (s.startsWith("hl7.fhir.uv.extensions.") && VersionUtilities.versionsMatch(v, v2)) { 812 return true; 813 } 814 } 815 } 816 return false; 817 818 } 819 820 @Override 821 public boolean hasPackage(PackageInformation pack) { 822 return false; 823 } 824 825 @Override 826 public PackageInformation getPackage(String id, String ver) { 827 return null; 828 } 829 830 public boolean isAllowLazyLoading() { 831 return allowLazyLoading; 832 } 833 834 public void setAllowLazyLoading(boolean allowLazyLoading) { 835 this.allowLazyLoading = allowLazyLoading; 836 } 837 838 public String loadedPackageSummary() { 839 return loadedPackages.toString(); 840 } 841 842 @Override 843 public String getSpecUrl() { 844 return VersionUtilities.getSpecUrl(getVersion())+"/"; 845 } 846 847 848 849} 850