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