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