001package org.hl7.fhir.validation; 002 003import java.io.ByteArrayInputStream; 004import java.io.ByteArrayOutputStream; 005import java.io.FileNotFoundException; 006import java.io.FileOutputStream; 007import java.io.IOException; 008import java.io.InputStream; 009import java.io.OutputStream; 010import java.io.PrintWriter; 011import java.net.URISyntaxException; 012import java.util.ArrayList; 013import java.util.Arrays; 014import java.util.Date; 015import java.util.HashMap; 016import java.util.List; 017import java.util.Locale; 018import java.util.Map; 019import java.util.UUID; 020 021import org.fhir.ucum.UcumEssenceService; 022import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_50; 023import org.hl7.fhir.convertors.factory.VersionConvertorFactory_14_50; 024import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50; 025import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; 026import org.hl7.fhir.convertors.txClient.TerminologyClientFactory; 027import org.hl7.fhir.exceptions.DefinitionException; 028import org.hl7.fhir.exceptions.FHIRException; 029import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; 030import org.hl7.fhir.r5.context.ContextUtilities; 031import org.hl7.fhir.r5.context.IWorkerContext; 032import org.hl7.fhir.r5.context.IWorkerContextManager; 033import org.hl7.fhir.r5.context.SimpleWorkerContext; 034import org.hl7.fhir.r5.context.SystemOutLoggingService; 035import org.hl7.fhir.r5.elementmodel.Element; 036import org.hl7.fhir.r5.elementmodel.Manager; 037import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat; 038import org.hl7.fhir.r5.elementmodel.ObjectConverter; 039import org.hl7.fhir.r5.elementmodel.ParserBase; 040import org.hl7.fhir.r5.elementmodel.SHCParser; 041import org.hl7.fhir.r5.formats.FormatUtilities; 042import org.hl7.fhir.r5.formats.IParser.OutputStyle; 043import org.hl7.fhir.r5.formats.JsonParser; 044import org.hl7.fhir.r5.formats.XmlParser; 045import org.hl7.fhir.r5.model.Base; 046import org.hl7.fhir.r5.model.Bundle; 047import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent; 048import org.hl7.fhir.r5.model.CanonicalResource; 049import org.hl7.fhir.r5.model.Coding; 050import org.hl7.fhir.r5.model.DomainResource; 051import org.hl7.fhir.r5.model.ElementDefinition; 052import org.hl7.fhir.r5.model.ExpressionNode; 053import org.hl7.fhir.r5.model.ImplementationGuide; 054import org.hl7.fhir.r5.model.OperationOutcome; 055import org.hl7.fhir.r5.model.PackageInformation; 056import org.hl7.fhir.r5.model.Parameters; 057import org.hl7.fhir.r5.model.Resource; 058import org.hl7.fhir.r5.model.StructureDefinition; 059import org.hl7.fhir.r5.model.StructureMap; 060import org.hl7.fhir.r5.model.ValueSet; 061import org.hl7.fhir.r5.renderers.RendererFactory; 062import org.hl7.fhir.r5.renderers.utils.RenderingContext; 063import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; 064import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; 065import org.hl7.fhir.r5.utils.EOperationOutcome; 066import org.hl7.fhir.r5.utils.FHIRPathEngine; 067import org.hl7.fhir.r5.utils.ToolingExtensions; 068import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities; 069import org.hl7.fhir.r5.utils.validation.BundleValidationRule; 070import org.hl7.fhir.r5.utils.validation.IResourceValidator; 071import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor; 072import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher; 073import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel; 074import org.hl7.fhir.r5.utils.validation.constants.BindingKind; 075import org.hl7.fhir.r5.utils.validation.constants.CheckDisplayOption; 076import org.hl7.fhir.r5.utils.validation.constants.CodedContentValidationPolicy; 077import org.hl7.fhir.r5.utils.validation.constants.ContainedReferenceValidationPolicy; 078import org.hl7.fhir.r5.utils.validation.constants.IdStatus; 079import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy; 080import org.hl7.fhir.utilities.FhirPublication; 081import org.hl7.fhir.utilities.IniFile; 082import org.hl7.fhir.utilities.SIDUtilities; 083import org.hl7.fhir.utilities.SimpleHTTPClient; 084import org.hl7.fhir.utilities.SimpleHTTPClient.HTTPResult; 085import org.hl7.fhir.utilities.TextFile; 086import org.hl7.fhir.utilities.TimeTracker; 087import org.hl7.fhir.utilities.Utilities; 088import org.hl7.fhir.utilities.VersionUtilities; 089import org.hl7.fhir.utilities.npm.CommonPackages; 090import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; 091import org.hl7.fhir.utilities.npm.NpmPackage; 092import org.hl7.fhir.utilities.npm.ToolsVersion; 093import org.hl7.fhir.utilities.validation.ValidationMessage; 094import org.hl7.fhir.utilities.xhtml.XhtmlComposer; 095import org.hl7.fhir.validation.BaseValidator.ValidationControl; 096import org.hl7.fhir.validation.cli.model.HtmlInMarkdownCheck; 097import org.hl7.fhir.validation.cli.services.IPackageInstaller; 098import org.hl7.fhir.validation.cli.utils.ProfileLoader; 099import org.hl7.fhir.validation.cli.utils.QuestionnaireMode; 100import org.hl7.fhir.validation.cli.utils.SchemaValidator; 101import org.hl7.fhir.validation.cli.utils.ValidationLevel; 102import org.hl7.fhir.validation.instance.InstanceValidator; 103import org.hl7.fhir.validation.instance.utils.ValidatorHostContext; 104import org.xml.sax.SAXException; 105 106import lombok.Getter; 107import lombok.Setter; 108import lombok.With; 109import lombok.experimental.Accessors; 110 111/* 112Copyright (c) 2011+, HL7, Inc 113All rights reserved. 114 115Redistribution and use in source and binary forms, with or without modification, 116are permitted provided that the following conditions are met: 117 118 * Redistributions of source code must retain the above copyright notice, this 119 list of conditions and the following disclaimer. 120 * Redistributions in binary form must reproduce the above copyright notice, 121 this list of conditions and the following disclaimer in the documentation 122 and/or other materials provided with the distribution. 123 * Neither the name of HL7 nor the names of its contributors may be used to 124 endorse or promote products derived from this software without specific 125 prior written permission. 126 127THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 128ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 129WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 130IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 131INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 132NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 133PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 134WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 135ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 136POSSIBILITY OF SUCH DAMAGE. 137 138*/ 139 140/** 141 * This is just a wrapper around the InstanceValidator class for convenient use 142 * <p> 143 * The following resource formats are supported: XML, JSON, Turtle 144 * The following versions are supported: 1.0.2, 1.4.0, 3.0.2, 4.0.1, and current 145 * <p> 146 * Note: the validation engine is intended to be threadsafe 147 * To Use: 148 * <p> 149 * 1/ Initialize 150 * ValidationEngine validator = new ValidationEngine(src); 151 * - this must be the packageId of the relevant core specification 152 * for the version you want to validate against (e.g. hl7.fhir.r4.core) 153 * <p> 154 * validator.connectToTSServer(txServer); 155 * - this is optional; in the absence of a terminology service, snomed, loinc etc will not be validated 156 * <p> 157 * validator.loadIg(src); 158 * - call this any number of times for the Implementation Guide(s) of interest. 159 * - See https://confluence.hl7.org/display/FHIR/Using+the+FHIR+Validator for documentation about the src parameter (-ig parameter) 160 * <p> 161 * validator.loadQuestionnaire(src) 162 * - url or filename of a questionnaire to load. Any loaded questionnaires will be used while validating 163 * <p> 164 * validator.setNative(doNative); 165 * - whether to do xml/json/rdf schema validation as well 166 * <p> 167 * You only need to do this initialization once. You can validate as many times as you like 168 * <p> 169 * 2. validate 170 * validator.validate(src, profiles); 171 * - source (as stream, byte[]), or url or filename of a resource to validate. 172 * Also validate against any profiles (as canonical URLS, equivalent to listing them in Resource.meta.profile) 173 * <p> 174 * if the source is provided as byte[] or stream, you need to provide a format too, though you can 175 * leave that as null, and the validator will guess 176 * <p> 177 * 3. Or, instead of validating, transform (see documentation and use in Validator.java) 178 * 179 * @author Grahame Grieve 180 */ 181@Accessors(chain = true) 182public class ValidationEngine implements IValidatorResourceFetcher, IValidationPolicyAdvisor, IPackageInstaller, IWorkerContextManager.IPackageLoadingTracker { 183 184 @Getter @Setter private SimpleWorkerContext context; 185 @Getter @Setter private Map<String, byte[]> binaries = new HashMap<>(); 186 @Getter @Setter private boolean doNative; 187 @Getter @Setter private boolean noInvariantChecks; 188 @Getter @Setter private boolean wantInvariantInMessage; 189 @Getter @Setter private boolean hintAboutNonMustSupport; 190 @Getter @Setter private boolean anyExtensionsAllowed = false; 191 @Getter @Setter private String version; 192 @Getter @Setter private String language; 193 @Setter private FilesystemPackageCacheManager pcm; 194 @Getter @Setter private PrintWriter mapLog; 195 @Getter @Setter private boolean debug = false; 196 @Getter @Setter private IValidatorResourceFetcher fetcher; 197 @Getter @Setter private IValidationPolicyAdvisor policyAdvisor; 198 @Getter @Setter private IWorkerContextManager.ICanonicalResourceLocator locator; 199 @Getter @Setter private boolean assumeValidRestReferences; 200 @Getter @Setter private boolean noExtensibleBindingMessages; 201 @Getter @Setter private boolean noUnicodeBiDiControlChars; 202 @Getter @Setter private boolean securityChecks; 203 @Getter @Setter private boolean crumbTrails; 204 @Getter @Setter private boolean forPublication; 205 @Getter @Setter private boolean allowExampleUrls; 206 @Getter @Setter private boolean showMessagesFromReferences; 207 @Getter @Setter private boolean doImplicitFHIRPathStringConversion; 208 @Getter @Setter private HtmlInMarkdownCheck htmlInMarkdownCheck; 209 @Getter @Setter private Locale locale; 210 @Getter @Setter private List<ImplementationGuide> igs = new ArrayList<>(); 211 @Getter @Setter private List<String> extensionDomains = new ArrayList<>(); 212 213 @Getter @Setter private boolean showTimes; 214 @Getter @Setter private List<BundleValidationRule> bundleValidationRules = new ArrayList<>(); 215 @Getter @Setter private QuestionnaireMode questionnaireMode; 216 @Getter @Setter private ValidationLevel level = ValidationLevel.HINTS; 217 @Getter @Setter private FHIRPathEngine fhirPathEngine; 218 @Getter @Setter private IgLoader igLoader; 219 @Getter @Setter private Coding jurisdiction; 220 221 222 /** 223 * Creating a validation engine is an expensive operation - takes seconds. 224 * Once you have a validation engine created, you can quickly clone it to 225 * get one that can load packages without affecting other uses 226 * 227 * @param other 228 * @throws FHIRException 229 * @throws IOException 230 */ 231 public ValidationEngine(ValidationEngine other) throws FHIRException, IOException { 232 super(); 233 context = new SimpleWorkerContext(other.context); 234 binaries.putAll(other.binaries); 235 doNative = other.doNative; 236 noInvariantChecks = other.noInvariantChecks; 237 wantInvariantInMessage = other.wantInvariantInMessage; 238 hintAboutNonMustSupport = other.hintAboutNonMustSupport; 239 anyExtensionsAllowed = other.anyExtensionsAllowed; 240 version = other.version; 241 language = other.language; 242 pcm = other.pcm; 243 mapLog = other.mapLog; 244 debug = other.debug; 245 fetcher = other.fetcher; 246 policyAdvisor = other.policyAdvisor; 247 locator = other.locator; 248 assumeValidRestReferences = other.assumeValidRestReferences; 249 noExtensibleBindingMessages = other.noExtensibleBindingMessages; 250 noUnicodeBiDiControlChars = other.noUnicodeBiDiControlChars; 251 securityChecks = other.securityChecks; 252 crumbTrails = other.crumbTrails; 253 forPublication = other.forPublication; 254 allowExampleUrls = other.allowExampleUrls; 255 showMessagesFromReferences = other.showMessagesFromReferences; 256 doImplicitFHIRPathStringConversion = other.doImplicitFHIRPathStringConversion; 257 htmlInMarkdownCheck = other.htmlInMarkdownCheck; 258 locale = other.locale; 259 igs.addAll(other.igs); 260 extensionDomains.addAll(other.extensionDomains); 261 showTimes = other.showTimes; 262 bundleValidationRules.addAll(other.bundleValidationRules); 263 questionnaireMode = other.questionnaireMode; 264 level = other.level; 265 fhirPathEngine = other.fhirPathEngine; 266 igLoader = other.igLoader; 267 jurisdiction = other.jurisdiction; 268 } 269 270 /** 271 * Systems that host the ValidationEngine can use this to control what validation the validator performs. 272 * <p> 273 * Using this, you can turn particular kinds of validation on and off. In addition, you can override 274 * the error | warning | hint level and make it a different level. 275 * <p> 276 * Each entry has 277 * * 'allowed': a boolean flag. if this is false, the Validator will not report the error. 278 * * 'level' : set to error, warning, information 279 * <p> 280 * Entries are registered by ID, using the IDs in /org.hl7.fhir.utilities/src/main/resources/Messages.properties 281 * <p> 282 * This feature is not supported by the validator CLI - and won't be. It's for systems hosting 283 * the validation framework in their own implementation context 284 */ 285 @Getter @Setter private Map<String, ValidationControl> validationControl = new HashMap<>(); 286 private Map<String, Boolean> resolvedUrls = new HashMap<>(); 287 288 private ValidationEngine() { 289 290 } 291 292 public static class ValidationEngineBuilder { 293 294 @With 295 private final String terminologyCachePath; 296 297 @With 298 private final String userAgent; 299 300 @With 301 private final String version; 302 303 //All three of these may be required to instantiate a txServer 304 private final String txServer; 305 private final String txLog; 306 private final FhirPublication txVersion; 307 308 @With 309 private final TimeTracker timeTracker; 310 311 @With 312 private final boolean canRunWithoutTerminologyServer; 313 314 @With 315 private final IWorkerContext.ILoggingService loggingService; 316 317 @With 318 private boolean THO = true; 319 320 321 public ValidationEngineBuilder() { 322 terminologyCachePath = null; 323 userAgent = null; 324 version = null; 325 txServer = null; 326 txLog = null; 327 txVersion = null; 328 timeTracker = null; 329 canRunWithoutTerminologyServer = false; 330 loggingService = new SystemOutLoggingService(); 331 } 332 333 public ValidationEngineBuilder(String terminologyCachePath, String userAgent, String version, String txServer, String txLog, FhirPublication txVersion, TimeTracker timeTracker, boolean canRunWithoutTerminologyServer, IWorkerContext.ILoggingService loggingService, boolean THO) { 334 this.terminologyCachePath = terminologyCachePath; 335 this.userAgent = userAgent; 336 this.version = version; 337 this.txServer = txServer; 338 this.txLog = txLog; 339 this.txVersion = txVersion; 340 this.timeTracker = timeTracker; 341 this.canRunWithoutTerminologyServer = canRunWithoutTerminologyServer; 342 this.loggingService = loggingService; 343 this.THO = THO; 344 } 345 346 public ValidationEngineBuilder withTxServer(String txServer, String txLog, FhirPublication txVersion) { 347 return new ValidationEngineBuilder(terminologyCachePath, userAgent, version, txServer, txLog, txVersion, timeTracker, canRunWithoutTerminologyServer, loggingService, THO); 348 } 349 350 public ValidationEngine fromNothing() throws IOException { 351 ValidationEngine engine = new ValidationEngine(); 352 SimpleWorkerContext.SimpleWorkerContextBuilder contextBuilder = new SimpleWorkerContext.SimpleWorkerContextBuilder().withLoggingService(loggingService); 353 if (terminologyCachePath != null) 354 contextBuilder = contextBuilder.withTerminologyCachePath(terminologyCachePath); 355 engine.setContext(contextBuilder.build()); 356 engine.initContext(timeTracker); 357 engine.setIgLoader(new IgLoader(engine.getPcm(), engine.getContext(), engine.getVersion(), engine.isDebug())); 358 loadTx(engine); 359 return engine; 360 } 361 362 public ValidationEngine fromSource(String src) throws IOException, URISyntaxException { 363 ValidationEngine engine = new ValidationEngine(); 364 engine.loadCoreDefinitions(src, false, terminologyCachePath, userAgent, timeTracker, loggingService); 365 engine.getContext().setCanRunWithoutTerminology(canRunWithoutTerminologyServer); 366 engine.getContext().setPackageTracker(engine); 367 if (txServer != null) { 368 engine.setTerminologyServer(txServer, txLog, txVersion); 369 } 370 engine.setVersion(version); 371 engine.setIgLoader(new IgLoader(engine.getPcm(), engine.getContext(), engine.getVersion(), engine.isDebug())); 372 if (THO) { 373 loadTx(engine); 374 } 375 return engine; 376 } 377 378 private void loadTx(ValidationEngine engine) throws FHIRException, IOException { 379 String pid = null; 380 if (VersionUtilities.isR3Ver(version)) { 381 pid = "hl7.terminology.r3"; 382 } 383 if (VersionUtilities.isR4Ver(version)) { 384 pid = "hl7.terminology.r4"; 385 } 386 if (VersionUtilities.isR4BVer(version)) { 387 pid = "hl7.terminology.r4"; 388 } 389 if (VersionUtilities.isR5Ver(version)) { 390 pid = "hl7.terminology.r5"; 391 } 392 if (pid != null) { 393 engine.loadPackage(pid, null); 394 } 395 } 396 } 397 398 /** 399 * 400 * @param src 401 * @param recursive 402 * @param terminologyCachePath 403 * @param userAgent 404 * @param tt 405 * @param loggingService 406 * @throws FHIRException 407 * @throws IOException 408 * 409 * @see IgLoader#loadIgSource(String, boolean, boolean) loadIgSource for detailed description of the src parameter 410 */ 411 private void loadCoreDefinitions(String src, boolean recursive, String terminologyCachePath, String userAgent, TimeTracker tt, IWorkerContext.ILoggingService loggingService) throws FHIRException, IOException { 412 NpmPackage npm = getPcm().loadPackage(src, null); 413 if (npm != null) { 414 version = npm.fhirVersion(); 415 SimpleWorkerContext.SimpleWorkerContextBuilder contextBuilder = new SimpleWorkerContext.SimpleWorkerContextBuilder().withLoggingService(loggingService); 416 if (terminologyCachePath != null) 417 contextBuilder = contextBuilder.withTerminologyCachePath(terminologyCachePath); 418 if (userAgent != null) { 419 contextBuilder.withUserAgent(userAgent); 420 } 421 context = contextBuilder.fromPackage(npm, ValidatorUtils.loaderForVersion(version)); 422 } else { 423 Map<String, byte[]> source = igLoader.loadIgSource(src, recursive, true); 424 if (version == null) { 425 version = getVersionFromPack(source); 426 } 427 SimpleWorkerContext.SimpleWorkerContextBuilder contextBuilder = new SimpleWorkerContext.SimpleWorkerContextBuilder(); 428 if (terminologyCachePath != null) 429 contextBuilder = contextBuilder.withTerminologyCachePath(terminologyCachePath); 430 if (userAgent != null) { 431 contextBuilder.withUserAgent(userAgent); 432 } 433 context = contextBuilder.fromDefinitions(source, ValidatorUtils.loaderForVersion(version), new PackageInformation(src, new Date())); 434 ValidatorUtils.grabNatives(getBinaries(), source, "http://hl7.org/fhir"); 435 } 436 // ucum-essence.xml should be in the class path. if it's not, ask about how to sort this out 437 // on https://chat.fhir.org/#narrow/stream/179167-hapi 438 try { 439 ClassLoader classLoader = ValidationEngine.class.getClassLoader(); 440 InputStream ue = classLoader.getResourceAsStream("ucum-essence.xml"); 441 context.setUcumService(new UcumEssenceService(ue)); 442 } catch (Exception e) { 443 throw new FHIRException("Error loading UCUM from embedded ucum-essence.xml: "+e.getMessage(), e); 444 } 445 initContext(tt); 446 } 447 448 protected void initContext(TimeTracker tt) throws IOException { 449 context.setCanNoTS(true); 450 context.setCacheId(UUID.randomUUID().toString()); 451 context.setAllowLoadingDuplicates(true); // because of Forge 452 context.setExpansionProfile(makeExpProfile()); 453 if (tt != null) { 454 context.setClock(tt); 455 } 456 NpmPackage npmX = getPcm().loadPackage(CommonPackages.ID_XVER, CommonPackages.VER_XVER); 457 context.loadFromPackage(npmX, null); 458 459 this.fhirPathEngine = new FHIRPathEngine(context); 460 } 461 462 private String getVersionFromPack(Map<String, byte[]> source) { 463 if (source.containsKey("version.info")) { 464 IniFile vi = new IniFile(new ByteArrayInputStream(removeBom(source.get("version.info")))); 465 return vi.getStringProperty("FHIR", "version"); 466 } else { 467 throw new Error("Missing version.info?"); 468 } 469 } 470 471 private byte[] removeBom(byte[] bs) { 472 if (bs.length > 3 && bs[0] == -17 && bs[1] == -69 && bs[2] == -65) 473 return Arrays.copyOfRange(bs, 3, bs.length); 474 else 475 return bs; 476 } 477 478 private Parameters makeExpProfile() { 479 Parameters ep = new Parameters(); 480 ep.addParameter("profile-url", "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"); // change this to blow the cache 481 // all defaults.... 482 return ep; 483 } 484 485 public String connectToTSServer(String url, String log, FhirPublication version) throws URISyntaxException, IOException, FHIRException { 486 return connectToTSServer(url, log, null, version); 487 } 488 489 public String connectToTSServer(String url, String log, String txCachePath, FhirPublication version) throws URISyntaxException, IOException, FHIRException { 490 context.setTlogging(false); 491 if (url == null) { 492 context.setCanRunWithoutTerminology(true); 493 context.setNoTerminologyServer(true); 494 return "n/a: No Terminology Server"; 495 } else { 496 try { 497 return context.connectToTSServer(TerminologyClientFactory.makeClient(url, context.getUserAgent(), version), log); 498 } catch (Exception e) { 499 if (context.isCanRunWithoutTerminology()) { 500 return "n/a: Running without Terminology Server (error: " + e.getMessage() + ")"; 501 } else 502 throw e; 503 } 504 } 505 } 506 507 public void loadProfile(String src) throws FHIRException, IOException { 508 if (context.hasResource(StructureDefinition.class, src)) 509 return; 510 if (context.hasResource(ImplementationGuide.class, src)) 511 return; 512 513 byte[] source = ProfileLoader.loadProfileSource(src); 514 FhirFormat fmt = FormatUtilities.determineFormat(source); 515 Resource r = FormatUtilities.makeParser(fmt).parse(source); 516 context.cacheResource(r); 517 } 518 519 // testing entry point 520 public OperationOutcome validate(FhirFormat format, InputStream stream, List<String> profiles) throws FHIRException, IOException, EOperationOutcome { 521 List<ValidationMessage> messages = new ArrayList<ValidationMessage>(); 522 InstanceValidator validator = getValidator(format); 523 validator.validate(null, messages, stream, format, asSdList(profiles)); 524 return ValidatorUtils.messagesToOutcome(messages, context, fhirPathEngine); 525 } 526 527 public List<StructureDefinition> asSdList(List<String> profiles) throws Error { 528 List<StructureDefinition> list = new ArrayList<>(); 529 if (profiles != null) { 530 for (String p : profiles) { 531 StructureDefinition sd = context.fetchResource(StructureDefinition.class, p); 532 if (sd == null) { 533 throw new Error("Unable to resolve profile " + p); 534 } 535 list.add(sd); 536 } 537 } 538 return list; 539 } 540 541 public OperationOutcome validate(String source, List<String> profiles) throws FHIRException, IOException { 542 List<String> l = new ArrayList<String>(); 543 l.add(source); 544 return (OperationOutcome) validate(l, profiles, null); 545 } 546 547 public Resource validate(List<String> sources, List<String> profiles, List<ValidationRecord> record) throws FHIRException, IOException { 548 if (profiles.size() > 0) { 549 System.out.println(" Profiles: " + profiles); 550 } 551 List<String> refs = new ArrayList<String>(); 552 boolean asBundle = ValidatorUtils.parseSources(sources, refs, context); 553 Bundle results = new Bundle(); 554 results.setType(Bundle.BundleType.COLLECTION); 555 for (String ref : refs) { 556 TimeTracker.Session tts = context.clock().start("validation"); 557 context.clock().milestone(); 558 System.out.println(" Validate " + ref); 559 Content cnt = igLoader.loadContent(ref, "validate", false); 560 try { 561 OperationOutcome outcome = validate(ref, cnt.focus, cnt.cntType, profiles, record); 562 ToolingExtensions.addStringExtension(outcome, ToolingExtensions.EXT_OO_FILE, ref); 563 System.out.println(" " + context.clock().milestone()); 564 results.addEntry().setResource(outcome); 565 tts.end(); 566 } catch (Exception e) { 567 System.out.println("Validation Infrastructure fail validating " + ref + ": " + e.getMessage()); 568 tts.end(); 569 throw new FHIRException(e); 570 } 571 } 572 if (asBundle) 573 return results; 574 else 575 return results.getEntryFirstRep().getResource(); 576 } 577 578 public OperationOutcome validate(byte[] source, FhirFormat cntType, List<String> profiles, List<ValidationMessage> messages) throws FHIRException, IOException, EOperationOutcome { 579 InstanceValidator validator = getValidator(cntType); 580 581 validator.validate(null, messages, new ByteArrayInputStream(source), cntType, asSdList(profiles)); 582 return ValidatorUtils.messagesToOutcome(messages, context, fhirPathEngine); 583 } 584 585 public OperationOutcome validate(String location, byte[] source, FhirFormat cntType, List<String> profiles, List<ValidationRecord> record) throws FHIRException, IOException, EOperationOutcome, SAXException { 586 List<ValidationMessage> messages = new ArrayList<ValidationMessage>(); 587 if (doNative) { 588 SchemaValidator.validateSchema(location, cntType, messages); 589 } 590 InstanceValidator validator = getValidator(cntType); 591 validator.validate(null, messages, new ByteArrayInputStream(source), cntType, asSdList(profiles)); 592 if (showTimes) { 593 System.out.println(location + ": " + validator.reportTimes()); 594 } 595 if (record != null) { 596 record.add(new ValidationRecord(location, messages)); 597 } 598 return ValidatorUtils.messagesToOutcome(messages, context, fhirPathEngine); 599 } 600 601 public OperationOutcome validate(String location, byte[] source, FhirFormat cntType, List<String> profiles, IdStatus resourceIdRule, boolean anyExtensionsAllowed, BestPracticeWarningLevel bpWarnings, CheckDisplayOption displayOption) throws FHIRException, IOException, EOperationOutcome, SAXException { 602 List<ValidationMessage> messages = new ArrayList<ValidationMessage>(); 603 if (doNative) { 604 SchemaValidator.validateSchema(location, cntType, messages); 605 } 606 InstanceValidator validator = getValidator(cntType); 607 validator.setResourceIdRule(resourceIdRule); 608 validator.setBestPracticeWarningLevel(bpWarnings); 609 validator.setCheckDisplay(displayOption); 610 validator.validate(null, messages, new ByteArrayInputStream(source), cntType, asSdList(profiles)); 611 return ValidatorUtils.messagesToOutcome(messages, context, fhirPathEngine); 612 } 613 614 public org.hl7.fhir.r5.elementmodel.Element transform(String source, String map) throws FHIRException, IOException { 615 Content cnt = igLoader.loadContent(source, "validate", false); 616 return transform(cnt.focus, cnt.cntType, map); 617 } 618 619 public StructureMap compile(String mapUri) throws FHIRException, IOException { 620 StructureMap map = context.fetchResource(StructureMap.class, mapUri); 621 return map; 622 } 623 624 public org.hl7.fhir.r5.elementmodel.Element transform(byte[] source, FhirFormat cntType, String mapUri) throws FHIRException, IOException { 625 List<Base> outputs = new ArrayList<>(); 626 StructureMapUtilities scu = new StructureMapUtilities(context, new TransformSupportServices(outputs, mapLog, context)); 627 StructureMap map = context.fetchResource(StructureMap.class, mapUri); 628 if (map == null) throw new Error("Unable to find map " + mapUri + " (Known Maps = " + context.listMapUrls() + ")"); 629 org.hl7.fhir.r5.elementmodel.Element resource = getTargetResourceFromStructureMap(map); 630 StructureDefinition sourceSD = getSourceResourceFromStructureMap(map); 631 ParserBase parser = Manager.makeParser(context, cntType); 632 if (sourceSD.getKind() == StructureDefinition.StructureDefinitionKind.LOGICAL) { 633 parser.setLogical(sourceSD); 634 } 635 org.hl7.fhir.r5.elementmodel.Element src = parser.parseSingle(new ByteArrayInputStream(source)); 636 scu.transform(null, src, map, resource); 637 resource.populatePaths(null); 638 return resource; 639 } 640 641 private org.hl7.fhir.r5.elementmodel.Element getTargetResourceFromStructureMap(StructureMap map) { 642 String targetTypeUrl = null; 643 for (StructureMap.StructureMapStructureComponent component : map.getStructure()) { 644 if (component.getMode() == StructureMap.StructureMapModelMode.TARGET) { 645 targetTypeUrl = component.getUrl(); 646 break; 647 } 648 } 649 650 if (targetTypeUrl == null) throw new FHIRException("Unable to determine resource URL for target type"); 651 652 StructureDefinition structureDefinition = null; 653 for (StructureDefinition sd : this.context.fetchResourcesByType(StructureDefinition.class)) { 654 if (sd.getUrl().equalsIgnoreCase(targetTypeUrl)) { 655 structureDefinition = sd; 656 break; 657 } 658 } 659 660 if (structureDefinition == null) throw new FHIRException("Unable to find StructureDefinition for target type ('" + targetTypeUrl + "')"); 661 662 return Manager.build(getContext(), structureDefinition); 663 } 664 665 private StructureDefinition getSourceResourceFromStructureMap(StructureMap map) { 666 StructureMap.StructureMapGroupComponent g = map.getGroup().get(0); 667 String type = null; 668 for (StructureMap.StructureMapGroupInputComponent inp : g.getInput()) { 669 if (inp.getMode() == StructureMap.StructureMapInputMode.SOURCE) 670 if (type != null) 671 throw new DefinitionException("This engine does not support multiple source inputs"); 672 else 673 type = inp.getType(); 674 } 675 676 String sourceTypeUrl = null; 677 for (StructureMap.StructureMapStructureComponent component : map.getStructure()) { 678 if (component.getMode() == StructureMap.StructureMapModelMode.SOURCE 679 && component.getAlias().equalsIgnoreCase(type)) { 680 sourceTypeUrl = component.getUrl(); 681 break; 682 } 683 } 684 685 StructureDefinition structureDefinition = null; 686 for (StructureDefinition sd : this.context.fetchResourcesByType(StructureDefinition.class)) { 687 if (sd.getUrl().equalsIgnoreCase(sourceTypeUrl)) { 688 structureDefinition = sd; 689 break; 690 } 691 } 692 693 if (structureDefinition == null) throw new FHIRException("Unable to find StructureDefinition for source type ('" + sourceTypeUrl + "')"); 694 return structureDefinition; 695 } 696 697 698 public Resource generate(String source, String version) throws FHIRException, IOException, EOperationOutcome { 699 Content cnt = igLoader.loadContent(source, "validate", false); 700 Resource res = igLoader.loadResourceByVersion(version, cnt.focus, source); 701 RenderingContext rc = new RenderingContext(context, null, null, "http://hl7.org/fhir", "", null, ResourceRendererMode.END_USER, GenerationRules.VALID_RESOURCE); 702 genResource(res, rc); 703 return (Resource) res; 704 } 705 706 public void genResource(Resource res, RenderingContext rc) throws IOException, EOperationOutcome { 707 if (res instanceof Bundle) { 708 Bundle bnd = (Bundle) res; 709 for (BundleEntryComponent be : bnd.getEntry()) { 710 if (be.hasResource()) { 711 genResource(be.getResource(), rc); 712 } 713 } 714 } else { 715 RendererFactory.factory(res, rc).render((DomainResource) res); 716 } 717 } 718 719 public void convert(String source, String output) throws FHIRException, IOException { 720 Content cnt = igLoader.loadContent(source, "validate", false); 721 Element e = Manager.parseSingle(context, new ByteArrayInputStream(cnt.focus), cnt.cntType); 722 Manager.compose(context, e, new FileOutputStream(output), (output.endsWith(".json") ? FhirFormat.JSON : FhirFormat.XML), OutputStyle.PRETTY, null); 723 } 724 725 public String evaluateFhirPath(String source, String expression) throws FHIRException, IOException { 726 Content cnt = igLoader.loadContent(source, "validate", false); 727 FHIRPathEngine fpe = this.getValidator(null).getFHIRPathEngine(); 728 Element e = Manager.parseSingle(context, new ByteArrayInputStream(cnt.focus), cnt.cntType); 729 ExpressionNode exp = fpe.parse(expression); 730 return fpe.evaluateToString(new ValidatorHostContext(context, e), e, e, e, exp); 731 } 732 733 public StructureDefinition snapshot(String source, String version) throws FHIRException, IOException { 734 Content cnt = igLoader.loadContent(source, "validate", false); 735 Resource res = igLoader.loadResourceByVersion(version, cnt.focus, Utilities.getFileNameForName(source)); 736 737 if (!(res instanceof StructureDefinition)) 738 throw new FHIRException("Require a StructureDefinition for generating a snapshot"); 739 StructureDefinition sd = (StructureDefinition) res; 740 StructureDefinition base = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition()); 741 742 new ProfileUtilities(context, null, null).setAutoFixSliceNames(true).generateSnapshot(base, sd, sd.getUrl(), null, sd.getName()); 743 return sd; 744 } 745 746 public CanonicalResource loadCanonicalResource(String source, String version) throws FHIRException, IOException { 747 Content cnt = igLoader.loadContent(source, "validate", false); 748 Resource res = igLoader.loadResourceByVersion(version, cnt.focus, Utilities.getFileNameForName(source)); 749 750 if (!(res instanceof CanonicalResource)) 751 throw new FHIRException("Require a CanonicalResource"); 752 return (CanonicalResource) res; 753 } 754 755 public void seeResource(Resource r) throws FHIRException { 756 context.cacheResource(r); 757 } 758 759 public void dropResource(String type, String id) { 760 context.dropResource(type, id); 761 } 762 763 public InstanceValidator getValidator(FhirFormat format) throws FHIRException, IOException { 764 InstanceValidator validator = new InstanceValidator(context, null, null); 765 validator.setHintAboutNonMustSupport(hintAboutNonMustSupport); 766 validator.setAnyExtensionsAllowed(anyExtensionsAllowed); 767 validator.getExtensionDomains().clear(); 768 validator.getExtensionDomains().addAll(extensionDomains); 769 validator.setNoInvariantChecks(isNoInvariantChecks()); 770 validator.setWantInvariantInMessage(isWantInvariantInMessage()); 771 validator.setValidationLanguage(language); 772 if (language != null) { 773 validator.getContext().setValidationMessageLanguage(Locale.forLanguageTag(language)); 774 } 775 validator.setAssumeValidRestReferences(assumeValidRestReferences); 776 validator.setNoExtensibleWarnings(noExtensibleBindingMessages); 777 validator.setSecurityChecks(securityChecks); 778 validator.setCrumbTrails(crumbTrails); 779 validator.setForPublication(forPublication); 780 validator.setAllowExamples(allowExampleUrls); 781 validator.setShowMessagesFromReferences(showMessagesFromReferences); 782 validator.getContext().setLocale(locale); 783 validator.setFetcher(this); 784 validator.getImplementationGuides().addAll(igs); 785 validator.getBundleValidationRules().addAll(bundleValidationRules); 786 validator.getValidationControl().putAll(validationControl); 787 validator.setQuestionnaireMode(questionnaireMode); 788 validator.setLevel(level); 789 validator.setHtmlInMarkdownCheck(htmlInMarkdownCheck); 790 validator.setNoUnicodeBiDiControlChars(noUnicodeBiDiControlChars); 791 validator.setDoImplicitFHIRPathStringConversion(doImplicitFHIRPathStringConversion); 792 if (format == FhirFormat.SHC) { 793 igLoader.loadIg(getIgs(), getBinaries(), SHCParser.CURRENT_PACKAGE, true); 794 } 795 validator.setJurisdiction(jurisdiction); 796 validator.setLogProgress(true); 797 return validator; 798 } 799 800 public void prepare() { 801 for (StructureDefinition sd : new ContextUtilities(context).allStructures()) { 802 try { 803 makeSnapshot(sd); 804 } catch (Exception e) { 805 System.out.println("Process Note: Unable to generate snapshot for " + sd.present() + ": " + e.getMessage()); 806 if (context.getLogger().isDebugLogging()) { 807 e.printStackTrace(); 808 } 809 } 810 } 811 } 812 813 private void makeSnapshot(StructureDefinition sd) throws DefinitionException, FHIRException { 814 if (sd.hasSnapshot()) 815 return; 816 StructureDefinition sdb = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition()); 817 if (sdb != null) { 818 makeSnapshot(sdb); 819 new ProfileUtilities(context, null, null).setAutoFixSliceNames(true).generateSnapshot(sdb, sd, sd.getUrl(), null, sd.getName()); 820 } 821 } 822 823 public void handleOutput(Resource r, String output, String version) throws FHIRException, IOException { 824 if (output.startsWith("http://")) { 825 ByteArrayOutputStream bs = new ByteArrayOutputStream(); 826 handleOutputToStream(r, output, bs, version); 827 SimpleHTTPClient http = new SimpleHTTPClient(); 828 HTTPResult res = http.post(output, "application/fhir+xml", bs.toByteArray(), "application/fhir+xml"); 829 res.checkThrowException(); 830 } else { 831 FileOutputStream s = new FileOutputStream(output); 832 handleOutputToStream(r, output, s, version); 833 } 834 } 835 836 private void handleOutputToStream(Resource r, String fn, OutputStream s, String version) throws FHIRException, IOException { 837 if (fn.endsWith(".html") || fn.endsWith(".htm") && r instanceof DomainResource) 838 new XhtmlComposer(XhtmlComposer.HTML, true).compose(s, ((DomainResource) r).getText().getDiv()); 839 else if (VersionUtilities.isR3Ver(version)) { 840 org.hl7.fhir.dstu3.model.Resource res = VersionConvertorFactory_30_50.convertResource(r); 841 if (fn.endsWith(".xml") && !fn.endsWith("template.xml")) 842 new org.hl7.fhir.dstu3.formats.XmlParser().setOutputStyle(org.hl7.fhir.dstu3.formats.IParser.OutputStyle.PRETTY).compose(s, res); 843 else if (fn.endsWith(".json") && !fn.endsWith("template.json")) 844 new org.hl7.fhir.dstu3.formats.JsonParser().setOutputStyle(org.hl7.fhir.dstu3.formats.IParser.OutputStyle.PRETTY).compose(s, res); 845 else if (fn.endsWith(".txt") || fn.endsWith(".map")) 846 TextFile.stringToStream(org.hl7.fhir.dstu3.utils.StructureMapUtilities.render((org.hl7.fhir.dstu3.model.StructureMap) res), s, false); 847 else 848 throw new FHIRException("Unsupported format for " + fn); 849 } else if (VersionUtilities.isR4Ver(version)) { 850 org.hl7.fhir.r4.model.Resource res = VersionConvertorFactory_40_50.convertResource(r); 851 if (fn.endsWith(".xml") && !fn.endsWith("template.xml")) 852 new org.hl7.fhir.r4.formats.XmlParser().setOutputStyle(org.hl7.fhir.r4.formats.IParser.OutputStyle.PRETTY).compose(s, res); 853 else if (fn.endsWith(".json") && !fn.endsWith("template.json")) 854 new org.hl7.fhir.r4.formats.JsonParser().setOutputStyle(org.hl7.fhir.r4.formats.IParser.OutputStyle.PRETTY).compose(s, res); 855 else if (fn.endsWith(".txt") || fn.endsWith(".map")) 856 TextFile.stringToStream(org.hl7.fhir.r4.utils.StructureMapUtilities.render((org.hl7.fhir.r4.model.StructureMap) res), s, false); 857 else 858 throw new FHIRException("Unsupported format for " + fn); 859 } else if (VersionUtilities.isR2BVer(version)) { 860 org.hl7.fhir.dstu2016may.model.Resource res = VersionConvertorFactory_14_50.convertResource(r); 861 if (fn.endsWith(".xml") && !fn.endsWith("template.xml")) 862 new org.hl7.fhir.dstu2016may.formats.XmlParser().setOutputStyle(org.hl7.fhir.dstu2016may.formats.IParser.OutputStyle.PRETTY).compose(s, res); 863 else if (fn.endsWith(".json") && !fn.endsWith("template.json")) 864 new org.hl7.fhir.dstu2016may.formats.JsonParser().setOutputStyle(org.hl7.fhir.dstu2016may.formats.IParser.OutputStyle.PRETTY).compose(s, res); 865 else 866 throw new FHIRException("Unsupported format for " + fn); 867 } else if (VersionUtilities.isR2Ver(version)) { 868 org.hl7.fhir.dstu2.model.Resource res = VersionConvertorFactory_10_50.convertResource(r, new org.hl7.fhir.convertors.misc.IGR2ConvertorAdvisor5()); 869 if (fn.endsWith(".xml") && !fn.endsWith("template.xml")) 870 new org.hl7.fhir.dstu2.formats.JsonParser().setOutputStyle(org.hl7.fhir.dstu2.formats.IParser.OutputStyle.PRETTY).compose(s, res); 871 else if (fn.endsWith(".json") && !fn.endsWith("template.json")) 872 new org.hl7.fhir.dstu2.formats.JsonParser().setOutputStyle(org.hl7.fhir.dstu2.formats.IParser.OutputStyle.PRETTY).compose(s, res); 873 else 874 throw new FHIRException("Unsupported format for " + fn); 875 } else if (VersionUtilities.isR5Ver(version)) { 876 if (fn.endsWith(".xml") && !fn.endsWith("template.xml")) 877 new XmlParser().setOutputStyle(org.hl7.fhir.r5.formats.IParser.OutputStyle.PRETTY).compose(s, r); 878 else if (fn.endsWith(".json") && !fn.endsWith("template.json")) 879 new JsonParser().setOutputStyle(org.hl7.fhir.r5.formats.IParser.OutputStyle.PRETTY).compose(s, r); 880 else if (fn.endsWith(".txt") || fn.endsWith(".map")) 881 TextFile.stringToStream(StructureMapUtilities.render((org.hl7.fhir.r5.model.StructureMap) r), s, false); 882 else 883 throw new FHIRException("Unsupported format for " + fn); 884 } else 885 throw new FHIRException("Encountered unsupported configured version " + version + " loading " + fn); 886 887 s.close(); 888 } 889 890 public byte[] transformVersion(String source, String targetVer, FhirFormat format, Boolean canDoNative) throws FHIRException, IOException, Exception { 891 Content cnt = igLoader.loadContent(source, "validate", false); 892 org.hl7.fhir.r5.elementmodel.Element src = Manager.parseSingle(context, new ByteArrayInputStream(cnt.focus), cnt.cntType); 893 894 // if the src has a url, we try to use the java code 895 if ((canDoNative == null && src.hasChild("url")) || (canDoNative != null && canDoNative)) { 896 try { 897 if (VersionUtilities.isR2Ver(version)) { 898 return VersionConvertor.convertVersionNativeR2(targetVer, cnt, format); 899 } else if (VersionUtilities.isR2BVer(version)) { 900 return VersionConvertor.convertVersionNativeR2b(targetVer, cnt, format); 901 } else if (VersionUtilities.isR3Ver(version)) { 902 return VersionConvertor.convertVersionNativeR3(targetVer, cnt, format); 903 } else if (VersionUtilities.isR4Ver(version)) { 904 return VersionConvertor.convertVersionNativeR4(targetVer, cnt, format); 905 } else { 906 throw new FHIRException("Source version not supported yet: " + version); 907 } 908 } catch (Exception e) { 909 System.out.println("Conversion failed using Java convertor: " + e.getMessage()); 910 } 911 } 912 // ok, we try converting using the structure maps 913 System.out.println("Loading hl7.fhir.xver.r4"); 914 igLoader.loadIg(getIgs(), getBinaries(), "hl7.fhir.xver.r4", false); 915 String type = src.fhirType(); 916 String url = getMapId(type, targetVer); 917 List<Base> outputs = new ArrayList<Base>(); 918 StructureMapUtilities scu = new StructureMapUtilities(context, new TransformSupportServices(outputs, mapLog, context)); 919 StructureMap map = context.fetchResource(StructureMap.class, url); 920 if (map == null) 921 throw new Error("Unable to find map " + url + " (Known Maps = " + context.listMapUrls() + ")"); 922 org.hl7.fhir.r5.elementmodel.Element resource = getTargetResourceFromStructureMap(map); 923 scu.transform(null, src, map, resource); 924 ByteArrayOutputStream bs = new ByteArrayOutputStream(); 925 Manager.compose(context, resource, bs, format, OutputStyle.PRETTY, null); 926 return bs.toByteArray(); 927 } 928 929 private String getMapId(String type, String targetVer) { 930 if (VersionUtilities.isR2Ver(version)) { 931 if (VersionUtilities.isR3Ver(targetVer)) { 932 return "http://hl7.org/fhir/StructureMap/" + type + "2to3"; 933 } 934 } else if (VersionUtilities.isR3Ver(version)) { 935 if (VersionUtilities.isR2Ver(targetVer)) { 936 return "http://hl7.org/fhir/StructureMap/" + type + "3to2"; 937 } else if (VersionUtilities.isR4Ver(targetVer)) { 938 return "http://hl7.org/fhir/StructureMap/" + type + "3to4"; 939 } 940 } else if (VersionUtilities.isR4Ver(version)) { 941 if (VersionUtilities.isR3Ver(targetVer)) { 942 return "http://hl7.org/fhir/StructureMap/" + type + "4to3"; 943 } 944 } 945 throw new FHIRException("Source/Target version not supported: " + version + " -> " + targetVer); 946 } 947 948 public String setTerminologyServer(String src, String log, FhirPublication version) throws FHIRException, URISyntaxException, IOException { 949 return connectToTSServer(src, log, version); 950 } 951 952 public ValidationEngine setMapLog(String mapLog) throws FileNotFoundException { 953 if (mapLog != null) { 954 this.mapLog = new PrintWriter(mapLog); 955 } 956 return this; 957 } 958 959 public ValidationEngine setSnomedExtension(String sct) { 960 getContext().getExpansionParameters().addParameter("system-version", "http://snomed.info/sct|http://snomed.info/sct/" + sct); 961 return this; 962 } 963 964 public FilesystemPackageCacheManager getPcm() throws IOException { 965 if (pcm == null) { 966 //System.out.println("Creating Package manager?"); 967 pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION); 968 } 969 return pcm; 970 } 971 972 @Override 973 public byte[] fetchRaw(IResourceValidator validator, String source) throws IOException { 974 SimpleHTTPClient http = new SimpleHTTPClient(); 975 HTTPResult res = http.get(source); 976 res.checkThrowException(); 977 return res.getContent(); 978 } 979 980 @Override 981 public boolean packageExists(String id, String ver) throws IOException, FHIRException { 982 return getPcm().packageExists(id, ver); 983 } 984 985 @Override 986 public void loadPackage(String id, String ver) throws IOException, FHIRException { 987 igLoader.loadIg(getIgs(), getBinaries(),id + (ver == null ? "" : "#" + ver), true); 988 } 989 990 @Override 991 public Element fetch(IResourceValidator validator, Object appContext, String url) throws FHIRException, IOException { 992 Resource resource = context.fetchResource(Resource.class, url); 993 if (resource != null) { 994 return new ObjectConverter(context).convert(resource); 995 } 996 if (fetcher != null) { 997 return fetcher.fetch(validator, appContext, url); 998 } 999 return null; 1000 } 1001 1002 @Override 1003 public ReferenceValidationPolicy policyForReference(IResourceValidator validator, Object appContext, String path, String url) { 1004 Resource resource = context.fetchResource(StructureDefinition.class, url); 1005 if (resource != null) { 1006 return ReferenceValidationPolicy.CHECK_VALID; 1007 } 1008 if (!(url.contains("hl7.org") || url.contains("fhir.org"))) { 1009 return ReferenceValidationPolicy.IGNORE; 1010 } else if (policyAdvisor != null) { 1011 return policyAdvisor.policyForReference(validator, appContext, path, url); 1012 } else { 1013 return ReferenceValidationPolicy.CHECK_EXISTS_AND_TYPE; 1014 } 1015 } 1016 1017 @Override 1018 public ContainedReferenceValidationPolicy policyForContained(IResourceValidator validator, 1019 Object appContext, 1020 String containerType, 1021 String containerId, 1022 Element.SpecialElement containingResourceType, 1023 String path, 1024 String url) { 1025 return ContainedReferenceValidationPolicy.CHECK_VALID; 1026 } 1027 1028 @Override 1029 public CodedContentValidationPolicy policyForCodedContent(IResourceValidator validator, Object appContext, String stackPath, ElementDefinition definition, StructureDefinition structure, BindingKind kind, ValueSet valueSet, List<String> systems) { 1030 return CodedContentValidationPolicy.VALUESET; 1031 } 1032 1033 @Override 1034 public boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type) throws FHIRException { 1035 // some of this logic might take a while, and it's not going to change once loaded 1036 if (resolvedUrls .containsKey(type+"|"+url)) { 1037 return resolvedUrls.get(type+"|"+url); 1038 } 1039 if (!url.startsWith("http://") && !url.startsWith("https://")) { // ignore these 1040 resolvedUrls.put(type+"|"+url, true); 1041 return true; 1042 } 1043 if (context.fetchResource(Resource.class, url) != null) { 1044 resolvedUrls.put(type+"|"+url, true); 1045 return true; 1046 } 1047 if (SIDUtilities.isKnownSID(url) || 1048 Utilities.existsInList(url, "http://hl7.org/fhir/w5", "http://hl7.org/fhir/fivews", "http://hl7.org/fhir/workflow", "http://hl7.org/fhir/ConsentPolicy/opt-out", "http://hl7.org/fhir/ConsentPolicy/opt-in")) { 1049 resolvedUrls.put(type+"|"+url, true); 1050 return true; 1051 } 1052 if (Utilities.existsInList(url, "http://loinc.org", "http://unitsofmeasure.org", "http://snomed.info/sct")) { 1053 resolvedUrls.put(type+"|"+url, true); 1054 return true; 1055 } 1056 if (context.getNSUrlMap().containsKey(url)) { 1057 resolvedUrls.put(type+"|"+url, true); 1058 return true; 1059 } 1060 if (url.contains("example.org") || url.contains("acme.com")) { 1061 resolvedUrls.put(type+"|"+url, false); 1062 return false; // todo... how to access settings from here? 1063 } 1064 if (fetcher != null) { 1065 try { 1066 boolean ok = fetcher.resolveURL(validator, appContext, path, url, type); 1067 resolvedUrls.put(type+"|"+url, ok); 1068 return ok; 1069 } catch (Exception e) { 1070 e.printStackTrace(); 1071 resolvedUrls.put(type+"|"+url, false); 1072 return false; 1073 } 1074 } 1075 resolvedUrls.put(type+"|"+url, false); 1076 return false; 1077 } 1078 1079 1080 @Override 1081 public CanonicalResource fetchCanonicalResource(IResourceValidator validator, String url) throws URISyntaxException { 1082 Resource res = context.fetchResource(Resource.class, url); 1083 if (res != null) { 1084 if (res instanceof CanonicalResource) { 1085 return (CanonicalResource) res; 1086 } else { 1087 return null; 1088 } 1089 } 1090 return fetcher != null ? fetcher.fetchCanonicalResource(validator, url) : null; 1091 } 1092 1093 @Override 1094 public boolean fetchesCanonicalResource(IResourceValidator validator, String url) { 1095 return fetcher != null && fetcher.fetchesCanonicalResource(validator, url); 1096 } 1097 1098 @Override 1099 public void packageLoaded(String pid, String version) { 1100 resolvedUrls.clear(); 1101 1102 } 1103 1104}