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