diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/Publisher.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/Publisher.java index 340bbf4d..11aa5974 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/Publisher.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/Publisher.java @@ -63,10 +63,12 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import javax.annotation.Nonnull; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import net.sf.saxon.trans.SymbolicName; import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecutor; import org.apache.commons.exec.ExecuteWatchdog; @@ -386,19 +388,21 @@ * structure map * * validate all source files (including the IG itself) - * + * * for each source file: * generate all outputs * * generate summary file * * Documentation: see http://wiki.hl7.org/index.php?title=IG_Publisher_Documentation - * + * * @author Grahame Grieve */ public class Publisher implements IWorkerContext.ILoggingService, IReferenceResolver, IValidationProfileUsageTracker { + private static final String PACKAGE_CACHE_FOLDER_PARAM = "-package-cache-folder"; + public class ContainedResourceDetails { private String type; @@ -542,7 +546,7 @@ public class TypeParserR2 implements ITypeParser { @Override public Base parseType(String xml, String type) throws IOException, FHIRException { - org.hl7.fhir.dstu2.model.Type t = new org.hl7.fhir.dstu2.formats.XmlParser().parseType(xml, type); + org.hl7.fhir.dstu2.model.Type t = new org.hl7.fhir.dstu2.formats.XmlParser().parseType(xml, type); return VersionConvertorFactory_10_50.convertType(t); } @@ -556,7 +560,7 @@ public class TypeParserR14 implements ITypeParser { @Override public Base parseType(String xml, String type) throws IOException, FHIRException { - org.hl7.fhir.dstu2016may.model.Type t = new org.hl7.fhir.dstu2016may.formats.XmlParser().parseType(xml, type); + org.hl7.fhir.dstu2016may.model.Type t = new org.hl7.fhir.dstu2016may.formats.XmlParser().parseType(xml, type); return VersionConvertorFactory_14_50.convertType(t); } @Override @@ -569,7 +573,7 @@ public class TypeParserR3 implements ITypeParser { @Override public Base parseType(String xml, String type) throws IOException, FHIRException { - org.hl7.fhir.dstu3.model.Type t = new org.hl7.fhir.dstu3.formats.XmlParser().parseType(xml, type); + org.hl7.fhir.dstu3.model.Type t = new org.hl7.fhir.dstu3.formats.XmlParser().parseType(xml, type); return VersionConvertorFactory_30_50.convertType(t); } @Override @@ -582,7 +586,7 @@ public class TypeParserR4 implements ITypeParser { @Override public Base parseType(String xml, String type) throws IOException, FHIRException { - org.hl7.fhir.r4.model.Type t = new org.hl7.fhir.r4.formats.XmlParser().parseType(xml, type); + org.hl7.fhir.r4.model.Type t = new org.hl7.fhir.r4.formats.XmlParser().parseType(xml, type); return VersionConvertorFactory_40_50.convertType(t); } @Override @@ -598,7 +602,7 @@ public class TypeParserR4B implements ITypeParser { @Override public Base parseType(String xml, String type) throws IOException, FHIRException { - org.hl7.fhir.r4b.model.DataType t = new org.hl7.fhir.r4b.formats.XmlParser().parseType(xml, type); + org.hl7.fhir.r4b.model.DataType t = new org.hl7.fhir.r4b.formats.XmlParser().parseType(xml, type); return VersionConvertorFactory_43_50.convertType(t); } @Override @@ -611,7 +615,7 @@ public class TypeParserR5 implements ITypeParser { @Override public Base parseType(String xml, String type) throws IOException, FHIRException { - return new org.hl7.fhir.r5.formats.XmlParser().parseType(xml, type); + return new org.hl7.fhir.r5.formats.XmlParser().parseType(xml, type); } @Override public Base parseType(Element base) throws FHIRFormatError, IOException, FHIRException { @@ -646,8 +650,8 @@ public enum GenerationTool { private static final String REDIRECT_SOURCE = "\r\n\r\n\r\n\r\n"+ "\r\n

See here: this link.

\r\n\r\n\r\n"; - private static final long JEKYLL_TIMEOUT = 60000 * 5; // 5 minutes.... - private static final long FSH_TIMEOUT = 60000 * 5; // 5 minutes.... + private static final long JEKYLL_TIMEOUT = 60000 * 5; // 5 minutes.... + private static final long FSH_TIMEOUT = 60000 * 5; // 5 minutes.... private static final int PRISM_SIZE_LIMIT = 16384; private static final String FIXED_CACHE_VERSION = "2"; // invalidating validation cache becaise it was incomplete @@ -700,7 +704,7 @@ public enum GenerationTool { private IGBuildMode mode; // for the IG publication infrastructure private SimpleFetcher fetcher = new SimpleFetcher(this); - private SimpleWorkerContext context; // + private SimpleWorkerContext context; // private DataRenderer dr; private InstanceValidator validator; private ProfileValidator pvalidator; @@ -742,7 +746,7 @@ public enum GenerationTool { private boolean makeQA = true; private boolean bundleReferencesResolve = true; private CqlSubSystem cql; - private File killFile; + private File killFile; private List pageFactories = new ArrayList<>(); private ILoggingService logger = this; @@ -893,6 +897,9 @@ public void setNoSushi(boolean noSushi) { @Setter private boolean generationOff; + @Getter + @Setter + private String packageCacheFolder = null; private String fmtDateTime = "yyyy-MM-dd HH:mm:ssZZZ"; private String fmtDate = "yyyy-MM-dd"; private DependentIGFinder dependentIgFinder; @@ -971,7 +978,7 @@ private String renderGlobals() { boolean list = sourceIg.getGlobal().size() > 1; if (list) { b.append("\r\n"); - } + } return b.toString(); } else { return "(none declared)"; @@ -990,7 +997,7 @@ private void packageTemplate() throws IOException { Utilities.createDirectory(outputDir); long startTime = System.nanoTime(); JsonObject qaJson = new JsonObject(); - StringBuilder txt = new StringBuilder(); + StringBuilder txt = new StringBuilder(); qaJson.add("url", templateInfo.asString("canonical")); txt.append("url = "+templateInfo.asString("canonical")+"\r\n"); qaJson.add("package-id", templateInfo.asString("name")); @@ -1087,7 +1094,7 @@ private void processTxLog(String path) throws FileNotFoundException, IOException f.print(tx); f.println(""); f.close(); - } + } } @@ -1119,25 +1126,25 @@ public void createIg() throws Exception, IOException, EOperationOutcome, FHIRExc generate(); clean(); dependentIgFinder.finish(outputDir, sourceIg.present()); - ValidationPresenter val = new ValidationPresenter(version, workingVersion(), igpkp, childPublisher == null? null : childPublisher.getIgpkp(), rootDir, npmName, childPublisher == null? null : childPublisher.npmName, + ValidationPresenter val = new ValidationPresenter(version, workingVersion(), igpkp, childPublisher == null? null : childPublisher.getIgpkp(), rootDir, npmName, childPublisher == null? null : childPublisher.npmName, IGVersionUtil.getVersion(), fetchCurrentIGPubVersion(), realmRules, previousVersionComparator, ipaComparator, - new DependencyRenderer(pcm, outputDir, npmName, templateManager, dependencyList, context, markdownEngine).render(publishedIg, true, false), new HTAAnalysisRenderer(context, outputDir, markdownEngine).render(publishedIg.getPackageId(), fileList, publishedIg.present()), + new DependencyRenderer(pcm, outputDir, npmName, templateManager, dependencyList, context, markdownEngine).render(publishedIg, true, false), new HTAAnalysisRenderer(context, outputDir, markdownEngine).render(publishedIg.getPackageId(), fileList, publishedIg.present()), new PublicationChecker(repoRoot, historyPage, markdownEngine).check(), renderGlobals(), copyrightYear, context, scanForR5Extensions(), modifierExtensions, generateDraftDependencies(), noNarrativeResources, noValidateResources, validationOff, generationOff, dependentIgFinder); tts.end(); if (isChild()) { - log("Built. "+tt.report()); + log("Built. "+tt.report()); } else { processTxLog(Utilities.path(destDir != null ? destDir : outputDir, "qa-tx.html")); - log("Built. "+tt.report()); + log("Built. "+tt.report()); log("Validation output in "+val.generate(sourceIg.getName(), errors, fileList, Utilities.path(destDir != null ? destDir : outputDir, "qa.html"), suppressedMessages)); } recordOutcome(null, val); - log("Finished"); + log("Finished"); } catch (Exception e) { try { - recordOutcome(e, null); + recordOutcome(e, null); } catch (Exception ex) { ex.printStackTrace(); } @@ -1166,10 +1173,10 @@ private void processTranslationOutputs() throws IOException { pt.translate(f, r); } } finally { - f.finish("translate"); + f.finish("translate"); } } - pt.finish(); + pt.finish(); } private Set scanForR5Extensions() { @@ -1188,7 +1195,7 @@ private void scanExamplesForR5(XVerExtensionManager xver, Set set) { scanElementForR5(xver, set, r.getElement()); } } finally { - f.finish("scanExamplesForR5"); + f.finish("scanExamplesForR5"); } } } @@ -1196,11 +1203,11 @@ private void scanExamplesForR5(XVerExtensionManager xver, Set set) { private void scanElementForR5(XVerExtensionManager xver, Set set, Element element) { if (element.fhirType().equals("Extension")) { String url = element.getChildValue("url"); - scanRefForR5(xver, set, url); + scanRefForR5(xver, set, url); } for (Element c : element.getChildren()) { scanElementForR5(xver, set, c); - } + } } private void scanProfilesForR5(XVerExtensionManager xver, Set set) { @@ -1215,9 +1222,9 @@ private void scanProfilesForR5(XVerExtensionManager xver, Set set) { } } } finally { - f.finish("scanProfilesForR5"); + f.finish("scanProfilesForR5"); } - } + } } @@ -1241,36 +1248,36 @@ private void scanProfileForR5(XVerExtensionManager xver, Set set, Struct scanRefForR5(xver, set, u.getValue()); } } - } + } } private void scanRefForR5(XVerExtensionManager xver, Set set, String url) { if (xver.matchingUrl(url) && xver.isR5(url)) { set.add(url); - } + } } private void processProvenanceDetails() throws Exception { for (FetchedFile f : fileList) { f.start("processProvenanceDetails"); - try { + try { for (FetchedResource r : f.getResources()) { if (!r.isExample()) { - if (r.fhirType().equals("Provenance")) { + if (r.fhirType().equals("Provenance")) { logDebugMessage(LogCategory.PROGRESS, "Process Provenance "+f.getName()+" : "+r.getId()); if (processProvenance(igpkp.getLinkFor(r, true), r.getElement(), r.getResource())) r.setProvenance(true); } else if (r.fhirType().equals("Bundle")) { if (processProvenanceEntries(f, r)) - r.setProvenance(true); + r.setProvenance(true); } } } } finally { - f.finish("processProvenanceDetails"); + f.finish("processProvenanceDetails"); } - } + } } public boolean processProvenanceEntries(FetchedFile f, FetchedResource r) throws Exception { @@ -1305,8 +1312,8 @@ private ProvenanceDetails processProvenanceForBundle(FetchedFile f, String path, d = target.getTitle() != null ? target.getTitle() : entity.hasDisplay() ? entity.getDisplay() : ref; } pd.getTargets().add(pd.new ProvenanceDetailsTarget(p, d)); - } - return pd; + } + return pd; } private boolean processProvenance(String path, Element resource, Resource r) { @@ -1399,7 +1406,7 @@ private void recordOutcome(Exception ex, ValidationPresenter val) { for (FetchedFile f : fileList) { JsonObject fj = new JsonObject(); j.forceArray("files").add(fj); - f.processReport(f, fj); + f.processReport(f, fj); } json = org.hl7.fhir.utilities.json.parser.JsonParser.compose(j, true); TextFile.stringToFile(json, Utilities.path(destDir != null ? destDir : outputDir, "qa-time-report.json"), false); @@ -1452,7 +1459,7 @@ public ResourceWithReference resolve(RenderingContext context, String url) { if (r.getResource() != null && r.getResource() instanceof CanonicalResource) { if (url.equals(((CanonicalResource) r.getResource()).getUrl())) { String path = igpkp.getLinkFor(r, true); - return new ResourceWithReference(path, new DirectWrappers.ResourceWrapperDirect(context, r.getResource())); + return new ResourceWithReference(path, new DirectWrappers.ResourceWrapperDirect(context, r.getResource())); } } } @@ -1561,7 +1568,7 @@ private void updatePageStatus(ImplementationGuideDefinitionPageComponent page, I } else { if (parentFmm != null && fmmExt == null) { fmm = parentFmm.copy(); - page.addExtension(new Extension(ToolingExtensions.EXT_FMM_LEVEL, fmm)); + page.addExtension(new Extension(ToolingExtensions.EXT_FMM_LEVEL, fmm)); } else if (fmmExt != null) fmm = fmmExt.getValueIntegerType(); } @@ -1673,7 +1680,7 @@ private void updateResourceStatus(FetchedResource r, IntegerType parentFmm, Code IntegerType newFmm = parentFmm.copy(); Extension e = new Extension(ToolingExtensions.EXT_FMM_LEVEL, newFmm); newFmm.addExtension(ToolingExtensions.EXT_FMM_DERIVED, new CanonicalType(parentCanonical)); - res.addExtension(e); + res.addExtension(e); } } @@ -1771,7 +1778,7 @@ private void updateResourceStatus(FetchedResource r, IntegerType parentFmm, Code ConceptMap cm = (ConceptMap)res; for (ConceptMapGroupComponent group: cm.getGroup()) { if (group.hasUnmapped() && group.getUnmapped().hasValueSet()) { - updateResourceStatus(group.getUnmapped().getValueSetElement(), fmm, status, statusNormVersion, res.getUrl()); + updateResourceStatus(group.getUnmapped().getValueSetElement(), fmm, status, statusNormVersion, res.getUrl()); } } break; @@ -1838,7 +1845,7 @@ private void updateResourceStatus(FetchedResource r, IntegerType parentFmm, Code updateResourceStatus(profile, fmm, status, statusNormVersion, res.getUrl()); } if (param.hasBinding() && param.getBinding().hasValueSet()) { - updateResourceStatus(param.getBinding().getValueSetElement(), fmm, status, statusNormVersion, res.getUrl()); + updateResourceStatus(param.getBinding().getValueSetElement(), fmm, status, statusNormVersion, res.getUrl()); } } break; @@ -1902,12 +1909,12 @@ private void updateResourceStatus(FetchedResource r, IntegerType parentFmm, Code case SubscriptionTopic: SubscriptionTopic st = (SubscriptionTopic)res; for (CanonicalType canonical: st.getDerivedFrom()) { - updateResourceStatus(canonical, fmm, status, statusNormVersion, res.getUrl()); + updateResourceStatus(canonical, fmm, status, statusNormVersion, res.getUrl()); } for (CanonicalType canonical: st.getDerivedFrom()) { for (SubscriptionTopicResourceTriggerComponent trigger: st.getResourceTrigger()) { for (SubscriptionTopicResourceTriggerCanFilterByComponent filter: trigger.getCanFilterBy()) { - updateResourceStatus(filter.getSearchParamName(), fmm, status, statusNormVersion, res.getUrl()); + updateResourceStatus(filter.getSearchParamName(), fmm, status, statusNormVersion, res.getUrl()); } } } @@ -1928,7 +1935,7 @@ private void updateResourceStatus(FetchedResource r, IntegerType parentFmm, Code for (CanonicalType valueSet: compose.getValueSet()) { updateResourceStatus(valueSet, fmm, status, statusNormVersion, res.getUrl()); } - } + } for (ConceptSetComponent compose: vs.getCompose().getExclude()) { if (compose.hasSystem()) updateResourceStatus(new CanonicalType(compose.getSystem()), fmm, status, statusNormVersion, res.getUrl()); @@ -1965,15 +1972,15 @@ public List getDescendantExtensions(Base e, String url) { } } return extensions; - } + } private void generateNarratives() throws Exception { Session tts = tt.start("narrative generation"); logDebugMessage(LogCategory.PROGRESS, "gen narratives"); for (FetchedFile f : fileList) { f.start("generateNarratives"); - try { - for (FetchedResource r : f.getResources()) { + try { + for (FetchedResource r : f.getResources()) { if (r.getExampleUri()==null || genExampleNarratives) { if (!passesNarrativeFilter(r)) { noNarrativeResources.add(r); @@ -2028,10 +2035,10 @@ private void generateNarratives() throws Exception { } } else { logDebugMessage(LogCategory.PROGRESS, "skipped narrative for "+f.getName()+" : "+r.getId()); - } + } } } finally { - f.finish("generateNarratives"); + f.finish("generateNarratives"); } } tts.end(); @@ -2318,7 +2325,7 @@ private String str(JsonObject obj, String name, String defValue) throws Exceptio } public void initialize() throws Exception { - pcm = new FilesystemPackageCacheManager(mode == null || mode == IGBuildMode.MANUAL || mode == IGBuildMode.PUBLICATION); + pcm = getFilesystemPackageCacheManager(); log("Build FHIR IG from "+configFile); if (mode == IGBuildMode.PUBLICATION) log("Build Formal Publication package, intended for "+getTargetOutput()); @@ -2333,7 +2340,7 @@ public void initialize() throws Exception { log("Also loading Packages from "+packagesFolder); pcm.loadFromFolder(packagesFolder); } - fetcher.setRootDir(rootDir); + fetcher.setRootDir(rootDir); fetcher.setResourceDirs(resourceDirs); if (configFile != null && focusDir().contains(" ")) { throw new Error("There is a space in the folder path: \""+focusDir()+"\". Please fix your directory arrangement to remove the space and try again"); @@ -2353,7 +2360,7 @@ public void initialize() throws Exception { IniFile ini = checkNewIg(); if (ini != null) { newIg = true; - initializeFromIg(ini); + initializeFromIg(ini); } else if (isTemplate()) initializeTemplate(); else @@ -2367,6 +2374,14 @@ public void initialize() throws Exception { } } + @Nonnull + private FilesystemPackageCacheManager getFilesystemPackageCacheManager() throws IOException { + if (getPackageCacheFolder() != null) { + return new FilesystemPackageCacheManager(getPackageCacheFolder()); + } + return new FilesystemPackageCacheManager(mode == null || mode == IGBuildMode.MANUAL || mode == IGBuildMode.PUBLICATION); + } + private Coding checkForJurisdiction() { String id = npmName; if (!id.startsWith("hl7.") || !id.contains(".")) { @@ -11554,6 +11569,8 @@ public static void main(String[] args) throws Exception { FhirSettings.setExplicitFilePath(CliParams.getNamedParam(args, FHIR_SETTINGS_PARAM)); } + + if (CliParams.hasNamedParam(args, "-gui")) { runGUI(args); // Returning here ends the main thread but leaves the GUI running @@ -11904,6 +11921,9 @@ public static void main(String[] args) throws Exception { if (CliParams.hasNamedParam(args, "-no-sushi")) { self.noSushi = true; } + if (CliParams.hasNamedParam(args, PACKAGE_CACHE_FOLDER_PARAM)) { + self.setPackageCacheFolder(CliParams.getNamedParam(args, PACKAGE_CACHE_FOLDER_PARAM)); + } try { self.execute(); if (CliParams.hasNamedParam(args, "-no-errors")) {