
001package org.hl7.fhir.r4.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.net.URISyntaxException; 039import java.util.ArrayList; 040import java.util.Arrays; 041import java.util.Collections; 042import java.util.HashMap; 043import java.util.HashSet; 044import java.util.List; 045import java.util.Map; 046import java.util.Set; 047import java.util.zip.ZipEntry; 048import java.util.zip.ZipInputStream; 049 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.r4.conformance.ProfileUtilities; 055import org.hl7.fhir.r4.conformance.ProfileUtilities.ProfileKnowledgeProvider; 056import org.hl7.fhir.r4.context.IWorkerContext.ILoggingService.LogCategory; 057import org.hl7.fhir.r4.formats.IParser; 058import org.hl7.fhir.r4.formats.JsonParser; 059import org.hl7.fhir.r4.formats.ParserType; 060import org.hl7.fhir.r4.formats.XmlParser; 061import org.hl7.fhir.r4.model.Bundle; 062import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; 063import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent; 064import org.hl7.fhir.r4.model.MetadataResource; 065import org.hl7.fhir.r4.model.Questionnaire; 066import org.hl7.fhir.r4.model.Resource; 067import org.hl7.fhir.r4.model.ResourceType; 068import org.hl7.fhir.r4.model.StructureDefinition; 069import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind; 070import org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule; 071import org.hl7.fhir.r4.model.StructureMap; 072import org.hl7.fhir.r4.model.StructureMap.StructureMapModelMode; 073import org.hl7.fhir.r4.model.StructureMap.StructureMapStructureComponent; 074import org.hl7.fhir.r4.terminologies.TerminologyClient; 075import org.hl7.fhir.r4.utils.INarrativeGenerator; 076import org.hl7.fhir.r4.utils.NarrativeGenerator; 077import org.hl7.fhir.r4.utils.validation.IResourceValidator; 078import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 079import org.hl7.fhir.utilities.Utilities; 080import org.hl7.fhir.utilities.filesystem.CSFileInputStream; 081import org.hl7.fhir.utilities.filesystem.ManagedFileAccess; 082import org.hl7.fhir.utilities.npm.NpmPackage; 083import org.hl7.fhir.utilities.validation.ValidationMessage; 084import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; 085import org.hl7.fhir.utilities.validation.ValidationMessage.Source; 086 087import ca.uhn.fhir.parser.DataFormatException; 088 089/* 090 * This is a stand alone implementation of worker context for use inside a tool. 091 * It loads from the validation package (validation-min.xml.zip), and has a 092 * very light client to connect to an open unauthenticated terminology service 093 */ 094 095@MarkedToMoveToAdjunctPackage 096public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerContext, ProfileKnowledgeProvider { 097 098 public interface IContextResourceLoader { 099 Bundle loadBundle(InputStream stream, boolean isJson) throws FHIRException, IOException; 100 } 101 102 public interface IValidatorFactory { 103 IResourceValidator makeValidator(IWorkerContext ctxts) throws FHIRException; 104 } 105 106 private Questionnaire questionnaire; 107 private Map<String, byte[]> binaries = new HashMap<String, byte[]>(); 108 private String version; 109 private String revision; 110 private String date; 111 private IValidatorFactory validatorFactory; 112 private boolean ignoreProfileErrors; 113 114 public SimpleWorkerContext() throws FileNotFoundException, IOException, FHIRException { 115 super(); 116 } 117 118 public SimpleWorkerContext(SimpleWorkerContext other) throws FileNotFoundException, IOException, FHIRException { 119 super(); 120 copy(other); 121 } 122 123 protected void copy(SimpleWorkerContext other) { 124 super.copy(other); 125 questionnaire = other.questionnaire; 126 binaries.putAll(other.binaries); 127 version = other.version; 128 revision = other.revision; 129 date = other.date; 130 validatorFactory = other.validatorFactory; 131 } 132 133 // -- Initializations 134 /** 135 * Load the working context from the validation pack 136 * 137 * @param path filename of the validation pack 138 * @return 139 * @throws IOException 140 * @throws FileNotFoundException 141 * @throws FHIRException 142 * @throws Exception 143 */ 144 public static SimpleWorkerContext fromPack(String path) throws FileNotFoundException, IOException, FHIRException { 145 SimpleWorkerContext res = new SimpleWorkerContext(); 146 res.loadFromPack(path, null); 147 return res; 148 } 149 150 public static SimpleWorkerContext fromNothing() throws FileNotFoundException, IOException, FHIRException { 151 SimpleWorkerContext res = new SimpleWorkerContext(); 152 return res; 153 } 154 155 public static SimpleWorkerContext fromPackage(NpmPackage pi, boolean allowDuplicates) 156 throws FileNotFoundException, IOException, FHIRException { 157 SimpleWorkerContext res = new SimpleWorkerContext(); 158 res.setAllowLoadingDuplicates(allowDuplicates); 159 res.loadFromPackage(pi, null); 160 return res; 161 } 162 163 public static SimpleWorkerContext fromPackage(NpmPackage pi) 164 throws FileNotFoundException, IOException, FHIRException { 165 SimpleWorkerContext res = new SimpleWorkerContext(); 166 res.loadFromPackage(pi, null); 167 return res; 168 } 169 170 public static SimpleWorkerContext fromPackage(NpmPackage pi, IContextResourceLoader loader) 171 throws FileNotFoundException, IOException, FHIRException { 172 SimpleWorkerContext res = new SimpleWorkerContext(); 173 res.setAllowLoadingDuplicates(true); 174 res.version = pi.getNpm().asString("version"); 175 res.loadFromPackage(pi, loader); 176 return res; 177 } 178 179 public static SimpleWorkerContext fromPack(String path, boolean allowDuplicates) 180 throws FileNotFoundException, IOException, FHIRException { 181 SimpleWorkerContext res = new SimpleWorkerContext(); 182 res.setAllowLoadingDuplicates(allowDuplicates); 183 res.loadFromPack(path, null); 184 return res; 185 } 186 187 public static SimpleWorkerContext fromPack(String path, IContextResourceLoader loader) 188 throws FileNotFoundException, IOException, FHIRException { 189 SimpleWorkerContext res = new SimpleWorkerContext(); 190 res.loadFromPack(path, loader); 191 return res; 192 } 193 194 public static SimpleWorkerContext fromClassPath() throws IOException, FHIRException { 195 SimpleWorkerContext res = new SimpleWorkerContext(); 196 res.loadFromStream(SimpleWorkerContext.class.getResourceAsStream("validation.json.zip"), null); 197 return res; 198 } 199 200 public static SimpleWorkerContext fromClassPath(String name) throws IOException, FHIRException { 201 InputStream s = SimpleWorkerContext.class.getResourceAsStream("/" + name); 202 SimpleWorkerContext res = new SimpleWorkerContext(); 203 res.loadFromStream(s, null); 204 return res; 205 } 206 207 public static SimpleWorkerContext fromDefinitions(Map<String, byte[]> source) throws IOException, FHIRException { 208 SimpleWorkerContext res = new SimpleWorkerContext(); 209 for (String name : source.keySet()) { 210 res.loadDefinitionItem(name, new ByteArrayInputStream(source.get(name)), null); 211 } 212 return res; 213 } 214 215 public static SimpleWorkerContext fromDefinitions(Map<String, byte[]> source, IContextResourceLoader loader) 216 throws FileNotFoundException, IOException, FHIRException { 217 SimpleWorkerContext res = new SimpleWorkerContext(); 218 for (String name : source.keySet()) { 219 try { 220 res.loadDefinitionItem(name, new ByteArrayInputStream(source.get(name)), loader); 221 } catch (Exception e) { 222 System.out.println("Error loading " + name + ": " + e.getMessage()); 223 throw new FHIRException("Error loading " + name + ": " + e.getMessage(), e); 224 } 225 } 226 return res; 227 } 228 229 private void loadDefinitionItem(String name, InputStream stream, IContextResourceLoader loader) 230 throws IOException, FHIRException { 231 if (name.endsWith(".xml")) 232 loadFromFile(stream, name, loader); 233 else if (name.endsWith(".json")) 234 loadFromFileJson(stream, name, loader); 235 else if (name.equals("version.info")) 236 readVersionInfo(stream); 237 else 238 loadBytes(name, stream); 239 } 240 241 public String connectToTSServer(TerminologyClient client, String log) throws URISyntaxException, FHIRException, IOException { 242 tlog("Connect to " + client.getAddress()); 243 txClient = client; 244 txLog = new HTMLClientLogger(log); 245 txClient.setLogger(txLog); 246 return txClient.getCapabilitiesStatementQuick().getSoftware().getVersion(); 247 } 248 249 public void loadFromFile(InputStream stream, String name, IContextResourceLoader loader) 250 throws IOException, FHIRException { 251 Resource f; 252 try { 253 if (loader != null) 254 f = loader.loadBundle(stream, false); 255 else { 256 XmlParser xml = new XmlParser(); 257 f = xml.parse(stream); 258 } 259 } catch (DataFormatException e1) { 260 throw new org.hl7.fhir.exceptions.FHIRFormatError("Error parsing " + name + ":" + e1.getMessage(), e1); 261 } catch (Exception e1) { 262 throw new org.hl7.fhir.exceptions.FHIRFormatError("Error parsing " + name + ":" + e1.getMessage(), e1); 263 } 264 if (f instanceof Bundle) { 265 Bundle bnd = (Bundle) f; 266 for (BundleEntryComponent e : bnd.getEntry()) { 267 if (e.getFullUrl() == null) { 268 logger.logDebugMessage(LogCategory.CONTEXT, "unidentified resource in " + name + " (no fullUrl)"); 269 } 270 cacheResource(e.getResource()); 271 } 272 } else if (f instanceof MetadataResource) { 273 MetadataResource m = (MetadataResource) f; 274 cacheResource(m); 275 } 276 } 277 278 private void loadFromFileJson(InputStream stream, String name, IContextResourceLoader loader) 279 throws IOException, FHIRException { 280 Bundle f = null; 281 try { 282 if (loader != null) 283 f = loader.loadBundle(stream, true); 284 else { 285 JsonParser json = new JsonParser(); 286 Resource r = json.parse(stream); 287 if (r instanceof Bundle) 288 f = (Bundle) r; 289 else 290 cacheResource(r); 291 } 292 } catch (FHIRFormatError e1) { 293 throw new org.hl7.fhir.exceptions.FHIRFormatError(e1.getMessage(), e1); 294 } 295 if (f != null) 296 for (BundleEntryComponent e : f.getEntry()) { 297 cacheResource(e.getResource()); 298 } 299 } 300 301 private void loadFromPack(String path, IContextResourceLoader loader) 302 throws FileNotFoundException, IOException, FHIRException { 303 loadFromStream(new CSFileInputStream(path), loader); 304 } 305 306 public void loadFromPackage(NpmPackage pi, IContextResourceLoader loader, String... types) 307 throws FileNotFoundException, IOException, FHIRException { 308 if (types.length == 0) 309 types = new String[] { "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", 310 "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem" }; 311 for (String s : pi.listResources(types)) { 312 loadDefinitionItem(s, pi.load("package", s), loader); 313 } 314 version = pi.version(); 315 } 316 317 public void loadFromFile(String file, IContextResourceLoader loader) throws IOException, FHIRException { 318 loadDefinitionItem(file, new CSFileInputStream(file), loader); 319 } 320 321 private void loadFromStream(InputStream stream, IContextResourceLoader loader) throws IOException, FHIRException { 322 ZipInputStream zip = new ZipInputStream(stream); 323 ZipEntry ze; 324 while ((ze = zip.getNextEntry()) != null) { 325 loadDefinitionItem(ze.getName(), zip, loader); 326 zip.closeEntry(); 327 } 328 zip.close(); 329 } 330 331 private void readVersionInfo(InputStream stream) throws IOException, DefinitionException { 332 byte[] bytes = IOUtils.toByteArray(stream); 333 binaries.put("version.info", bytes); 334 335 String[] vi = new String(bytes).split("\\r?\\n"); 336 for (String s : vi) { 337 if (s.startsWith("version=")) { 338 if (version == null) 339 version = s.substring(8); 340 else if (!version.equals(s.substring(8))) 341 throw new DefinitionException("Version mismatch. The context has version " + version 342 + " loaded, and the new content being loaded is version " + s.substring(8)); 343 } 344 if (s.startsWith("revision=")) 345 revision = s.substring(9); 346 if (s.startsWith("date=")) 347 date = s.substring(5); 348 } 349 } 350 351 private void loadBytes(String name, InputStream stream) throws IOException { 352 byte[] bytes = IOUtils.toByteArray(stream); 353 binaries.put(name, bytes); 354 } 355 356 @Override 357 public IParser getParser(ParserType type) { 358 switch (type) { 359 case JSON: 360 return newJsonParser(); 361 case XML: 362 return newXmlParser(); 363 default: 364 throw new Error("Parser Type " + type.toString() + " not supported"); 365 } 366 } 367 368 @Override 369 public IParser getParser(String type) { 370 if (type.equalsIgnoreCase("JSON")) 371 return new JsonParser(); 372 if (type.equalsIgnoreCase("XML")) 373 return new XmlParser(); 374 throw new Error("Parser Type " + type.toString() + " not supported"); 375 } 376 377 @Override 378 public IParser newJsonParser() { 379 return new JsonParser(); 380 } 381 382 @Override 383 public IParser newXmlParser() { 384 return new XmlParser(); 385 } 386 387 @Override 388 public INarrativeGenerator getNarrativeGenerator(String prefix, String basePath) { 389 return new NarrativeGenerator(prefix, basePath, this); 390 } 391 392 @Override 393 public IResourceValidator newValidator() throws FHIRException { 394 if (validatorFactory == null) 395 throw new Error("No validator configured"); 396 return validatorFactory.makeValidator(this); 397 } 398 399 @Override 400 public List<String> getResourceNames() { 401 List<String> result = new ArrayList<String>(); 402 for (StructureDefinition sd : listStructures()) { 403 if (sd.getKind() == StructureDefinitionKind.RESOURCE && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) 404 result.add(sd.getName()); 405 } 406 Collections.sort(result); 407 return result; 408 } 409 410 @Override 411 public List<String> getTypeNames() { 412 List<String> result = new ArrayList<String>(); 413 for (StructureDefinition sd : listStructures()) { 414 if (sd.getKind() != StructureDefinitionKind.LOGICAL && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) 415 result.add(sd.getName()); 416 } 417 Collections.sort(result); 418 return result; 419 } 420 421 @Override 422 public String getAbbreviation(String name) { 423 return "xxx"; 424 } 425 426 @Override 427 public boolean isDatatype(String typeSimple) { 428 // TODO Auto-generated method stub 429 return false; 430 } 431 432 @Override 433 public boolean isResource(String t) { 434 StructureDefinition sd; 435 try { 436 sd = fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + t); 437 } catch (Exception e) { 438 return false; 439 } 440 if (sd == null) 441 return false; 442 if (sd.getDerivation() == TypeDerivationRule.CONSTRAINT) 443 return false; 444 return sd.getKind() == StructureDefinitionKind.RESOURCE; 445 } 446 447 @Override 448 public boolean hasLinkFor(String typeSimple) { 449 return false; 450 } 451 452 @Override 453 public String getLinkFor(String corePath, String typeSimple) { 454 return null; 455 } 456 457 @Override 458 public BindingResolution resolveBinding(StructureDefinition profile, ElementDefinitionBindingComponent binding, 459 String path) { 460 return null; 461 } 462 463 @Override 464 public BindingResolution resolveBinding(StructureDefinition profile, String url, String path) { 465 return null; 466 } 467 468 @Override 469 public String getLinkForProfile(StructureDefinition profile, String url) { 470 return null; 471 } 472 473 public Questionnaire getQuestionnaire() { 474 return questionnaire; 475 } 476 477 public void setQuestionnaire(Questionnaire questionnaire) { 478 this.questionnaire = questionnaire; 479 } 480 481 @Override 482 public Set<String> typeTails() { 483 return new HashSet<String>( 484 Arrays.asList("Integer", "UnsignedInt", "PositiveInt", "Decimal", "DateTime", "Date", "Time", "Instant", 485 "String", "Uri", "Url", "Canonical", "Oid", "Uuid", "Id", "Boolean", "Code", "Markdown", "Base64Binary", 486 "Coding", "CodeableConcept", "Attachment", "Identifier", "Quantity", "SampledData", "Range", "Period", 487 "Ratio", "HumanName", "Address", "ContactPoint", "Timing", "Reference", "Annotation", "Signature", "Meta")); 488 } 489 490 @Override 491 public List<StructureDefinition> allStructures() { 492 List<StructureDefinition> result = new ArrayList<StructureDefinition>(); 493 Set<StructureDefinition> set = new HashSet<StructureDefinition>(); 494 for (StructureDefinition sd : listStructures()) { 495 if (!set.contains(sd)) { 496 try { 497 generateSnapshot(sd); 498 } catch (Exception e) { 499 System.out.println("Unable to generate snapshot for " + sd.getUrl() + " because " + e.getMessage()); 500 } 501 result.add(sd); 502 set.add(sd); 503 } 504 } 505 return result; 506 } 507 508 public void loadBinariesFromFolder(String folder) throws FileNotFoundException, Exception { 509 for (String n : ManagedFileAccess.file(folder).list()) { 510 loadBytes(n, ManagedFileAccess.inStream(Utilities.path(folder, n))); 511 } 512 } 513 514 public void loadBinariesFromFolder(NpmPackage pi) throws FileNotFoundException, Exception { 515 for (String n : pi.list("other")) { 516 loadBytes(n, pi.load("other", n)); 517 } 518 } 519 520 public void loadFromFolder(String folder) throws FileNotFoundException, Exception { 521 for (String n : ManagedFileAccess.file(folder).list()) { 522 if (n.endsWith(".json")) 523 loadFromFile(Utilities.path(folder, n), new JsonParser()); 524 else if (n.endsWith(".xml")) 525 loadFromFile(Utilities.path(folder, n), new XmlParser()); 526 } 527 } 528 529 private void loadFromFile(String filename, IParser p) throws FileNotFoundException, Exception { 530 Resource r; 531 try { 532 r = p.parse(ManagedFileAccess.inStream(filename)); 533 if (r.getResourceType() == ResourceType.Bundle) { 534 for (BundleEntryComponent e : ((Bundle) r).getEntry()) { 535 cacheResource(e.getResource()); 536 } 537 } else { 538 cacheResource(r); 539 } 540 } catch (Exception e) { 541 return; 542 } 543 } 544 545 public Map<String, byte[]> getBinaries() { 546 return binaries; 547 } 548 549 @Override 550 public boolean prependLinks() { 551 return false; 552 } 553 554 @Override 555 public boolean hasCache() { 556 return false; 557 } 558 559 @Override 560 public String getVersion() { 561 return version; 562 } 563 564 public List<StructureMap> findTransformsforSource(String url) { 565 List<StructureMap> res = new ArrayList<StructureMap>(); 566 for (StructureMap map : listTransforms()) { 567 boolean match = false; 568 boolean ok = true; 569 for (StructureMapStructureComponent t : map.getStructure()) { 570 if (t.getMode() == StructureMapModelMode.SOURCE) { 571 match = match || t.getUrl().equals(url); 572 ok = ok && t.getUrl().equals(url); 573 } 574 } 575 if (match && ok) 576 res.add(map); 577 } 578 return res; 579 } 580 581 public IValidatorFactory getValidatorFactory() { 582 return validatorFactory; 583 } 584 585 public void setValidatorFactory(IValidatorFactory validatorFactory) { 586 this.validatorFactory = validatorFactory; 587 } 588 589 @Override 590 public <T extends Resource> T fetchResource(Class<T> class_, String uri) { 591 T r = super.fetchResource(class_, uri); 592 if (r instanceof StructureDefinition) { 593 StructureDefinition p = (StructureDefinition) r; 594 try { 595 generateSnapshot(p); 596 } catch (Exception e) { 597 // not sure what to do in this case? 598 System.out.println("Unable to generate snapshot for " + uri + ": " + e.getMessage()); 599 } 600 } 601 return r; 602 } 603 604 public void generateSnapshot(StructureDefinition p) throws DefinitionException, FHIRException { 605 if (!p.hasSnapshot() && p.getKind() != StructureDefinitionKind.LOGICAL) { 606 if (!p.hasBaseDefinition()) 607 throw new DefinitionException("Profile " + p.getName() + " (" + p.getUrl() + ") has no base and no snapshot"); 608 StructureDefinition sd = fetchResource(StructureDefinition.class, p.getBaseDefinition()); 609 if (sd == null) 610 throw new DefinitionException("Profile " + p.getName() + " (" + p.getUrl() + ") base " + p.getBaseDefinition() 611 + " could not be resolved"); 612 List<ValidationMessage> msgs = new ArrayList<ValidationMessage>(); 613 List<String> errors = new ArrayList<String>(); 614 ProfileUtilities pu = new ProfileUtilities(this, msgs, this); 615 pu.setThrowException(false); 616 pu.sortDifferential(sd, p, p.getUrl(), errors); 617 for (String err : errors) 618 msgs.add(new ValidationMessage(Source.ProfileValidator, IssueType.EXCEPTION, p.getUserString("path"), 619 "Error sorting Differential: " + err, ValidationMessage.IssueSeverity.ERROR)); 620 pu.generateSnapshot(sd, p, p.getUrl(), Utilities.extractBaseUrl(sd.getUserString("path")), p.getName()); 621 for (ValidationMessage msg : msgs) { 622 if ((!ignoreProfileErrors && msg.getLevel() == ValidationMessage.IssueSeverity.ERROR) 623 || msg.getLevel() == ValidationMessage.IssueSeverity.FATAL) 624 throw new DefinitionException( 625 "Profile " + p.getName() + " (" + p.getUrl() + "). Error generating snapshot: " + msg.getMessage()); 626 } 627 if (!p.hasSnapshot()) 628 throw new FHIRException("Profile " + p.getName() + " (" + p.getUrl() + "). Error generating snapshot"); 629 pu = null; 630 } 631 } 632 633 public boolean isIgnoreProfileErrors() { 634 return ignoreProfileErrors; 635 } 636 637 public void setIgnoreProfileErrors(boolean ignoreProfileErrors) { 638 this.ignoreProfileErrors = ignoreProfileErrors; 639 } 640 641 public String listMapUrls() { 642 return Utilities.listCanonicalUrls(transforms.keySet()); 643 } 644 645}