diff --git a/pom.xml b/pom.xml index 2e334c05e..da9fb736e 100644 --- a/pom.xml +++ b/pom.xml @@ -201,6 +201,12 @@ ${cqframework.version} + + info.cqframework + cqf-fhir-npm + ${cqframework.version} + + info.cqframework elm-fhir diff --git a/tooling-cli/pom.xml b/tooling-cli/pom.xml index cc91e6e67..81d320728 100644 --- a/tooling-cli/pom.xml +++ b/tooling-cli/pom.xml @@ -9,7 +9,6 @@ 2.5.0-SNAPSHOT - org.opencds.cqf tooling-cli 2.5.0-SNAPSHOT jar diff --git a/tooling/pom.xml b/tooling/pom.xml index a00fbe9c2..e6da65614 100644 --- a/tooling/pom.xml +++ b/tooling/pom.xml @@ -7,10 +7,8 @@ org.opencds.cqf tooling-parent 2.5.0-SNAPSHOT - - org.opencds.cqf tooling 2.5.0-SNAPSHOT jar @@ -192,7 +190,7 @@ com.github.ben-manes.caffeine caffeine - 2.9.1 + 3.1.1 @@ -230,14 +228,20 @@ org.slf4j jcl-over-slf4j - 1.7.30 + 1.7.33 org.slf4j log4j-over-slf4j - 1.7.30 + 1.7.33 + + + + org.slf4j + slf4j-jdk14 + 1.7.33 @@ -251,6 +255,11 @@ elm-fhir + + info.cqframework + cqf-fhir-npm + + com.google.errorprone error_prone_annotations @@ -325,7 +334,7 @@ org.slf4j slf4j-simple - 1.7.30 + 2.0.5 true diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/dateroller/ResourceDataDateRoller.java b/tooling/src/main/java/org/opencds/cqf/tooling/dateroller/ResourceDataDateRoller.java index 4a1267e9c..ccce52930 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/dateroller/ResourceDataDateRoller.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/dateroller/ResourceDataDateRoller.java @@ -9,36 +9,40 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; +import java.util.List; public class ResourceDataDateRoller { private static Logger logger = LoggerFactory.getLogger(ResourceDataDateRoller.class); + private ResourceDataDateRoller() { + + } + public static void rollBundleDates(FhirContext fhirContext, IBaseResource iBaseResource) { - switch (fhirContext.getVersion().getVersion().name()) { - case "R4": - ArrayList r4ResourceArrayList = BundleUtils.getR4ResourcesFromBundle((org.hl7.fhir.r4.model.Bundle) iBaseResource); - r4ResourceArrayList.forEach(resource -> { - RollDatesR4.rollDatesInResource(resource); - }); + switch (fhirContext.getVersion().getVersion()) { + case R4: + List r4ResourceArrayList = BundleUtils.getR4ResourcesFromBundle((org.hl7.fhir.r4.model.Bundle) iBaseResource); + r4ResourceArrayList.forEach(RollDatesR4::rollDatesInResource); break; - case "Stu3": - ArrayList stu3resourceArrayList = BundleUtils.getStu3ResourcesFromBundle((org.hl7.fhir.dstu3.model.Bundle) iBaseResource); - stu3resourceArrayList.forEach(resource -> { - RollDatesDstu3.rollDatesInResource(resource); - }); + case DSTU3: + List stu3resourceArrayList = BundleUtils.getStu3ResourcesFromBundle((org.hl7.fhir.dstu3.model.Bundle) iBaseResource); + stu3resourceArrayList.forEach(RollDatesDstu3::rollDatesInResource); break; + default: throw new UnsupportedOperationException("Date Roller not supported for version " + + fhirContext.getVersion().getVersion().getFhirVersionString()); } } public static void rollResourceDates(FhirContext fhirContext, IBaseResource resource) { - switch (fhirContext.getVersion().getVersion().name()) { - case "R4": + switch (fhirContext.getVersion().getVersion()) { + case R4: RollDatesR4.rollDatesInResource(resource); break; - case "DSTU3": + case DSTU3: RollDatesDstu3.rollDatesInResource(resource); break; + default: throw new UnsupportedOperationException("Date Roller not supported for version " + + fhirContext.getVersion().getVersion().getFhirVersionString()); } } } diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/library/LibraryProcessor.java b/tooling/src/main/java/org/opencds/cqf/tooling/library/LibraryProcessor.java index 11a950e65..9a87ee28f 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/library/LibraryProcessor.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/library/LibraryProcessor.java @@ -41,7 +41,7 @@ public static String getId(String baseId) { private static Pattern getPattern() { if(pattern == null) { - String regex = "^[a-zA-Z]+[a-zA-Z0-9_\\-\\.]*"; + String regex = "^[a-zA-Z]+[a-zA-Z\\d_\\-.]*"; pattern = Pattern.compile(regex); } return pattern; @@ -92,8 +92,8 @@ public List refreshIgLibraryContent(BaseProcessor parentContext, Encodin public Boolean bundleLibraryDependencies(String path, FhirContext fhirContext, Map resources, Encoding encoding, boolean versioned) { String fileName = FilenameUtils.getName(path); - boolean prefixed = fileName.toLowerCase().startsWith("library-"); - Boolean shouldPersist = true; + boolean prefixed = fileName.toLowerCase().startsWith(ResourcePrefix); + boolean shouldPersist = true; try { Map dependencies = ResourceUtils.getDepLibraryResources(path, fhirContext, encoding, versioned, logger); // String currentResourceID = IOUtils.getTypeQualifiedResourceId(path, fhirContext); @@ -163,7 +163,7 @@ protected Library refreshGeneratedContent(Library sourceLibrary) { sourceLibrary.getParameter().clear(); sourceLibrary.getParameter().addAll(info.getParameters()); } else { - logMessage(String.format("No cql info found for ", fileName)); + logMessage("No cql info found for " + fileName); //f.getErrors().add(new ValidationMessage(ValidationMessage.Source.Publisher, ValidationMessage.IssueType.NOTFOUND, "Library", "No cql info found for "+f.getName(), ValidationMessage.IssueSeverity.ERROR)); } } @@ -176,14 +176,14 @@ protected List refreshGeneratedContent(List sourceLibraries) { } public List refreshGeneratedContent(String cqlDirectoryPath, String fhirVersion) { - List result = new ArrayList(); + List result = new ArrayList<>(); File input = new File(cqlDirectoryPath); if (input.exists() && input.isDirectory()) { result.add(input.getAbsolutePath()); } setBinaryPaths(result); - List libraries = new ArrayList(); + List libraries = new ArrayList<>(); return internalRefreshGeneratedContent(libraries); } @@ -217,7 +217,7 @@ private List internalRefreshGeneratedContent(List sourceLibrar newLibrary.setId(newLibrary.getName() + (versioned ? "-" + newLibrary.getVersion() : "")); setLibraryType(newLibrary); validateIdAlphaNumeric(newLibrary.getId()); - List attachments = new ArrayList(); + List attachments = new ArrayList<>(); Attachment attachment = new Attachment(); attachment.setContentType("application/elm+xml"); attachment.setData(fileInfo.getElm()); @@ -228,7 +228,7 @@ private List internalRefreshGeneratedContent(List sourceLibrar } } - List resources = new ArrayList(); + List resources = new ArrayList<>(); for (Library library : sourceLibraries) { resources.add(refreshGeneratedContent(library)); } @@ -250,6 +250,6 @@ private Attachment loadFile(String fn) throws IOException { } public List refreshLibraryContent(RefreshLibraryParameters params) { - return new ArrayList(); + return new ArrayList<>(); } } \ No newline at end of file diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/measure/MeasureProcessor.java b/tooling/src/main/java/org/opencds/cqf/tooling/measure/MeasureProcessor.java index 341bf7cdf..d2ccc37dc 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/measure/MeasureProcessor.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/measure/MeasureProcessor.java @@ -35,6 +35,7 @@ import ca.uhn.fhir.context.FhirContext; public class MeasureProcessor extends BaseProcessor { + private static final Logger logger = LoggerFactory.getLogger(MeasureProcessor.class); public static final String ResourcePrefix = "measure-"; public static final String MeasureTestGroupName = "measure"; protected List identifiers; @@ -42,7 +43,6 @@ public class MeasureProcessor extends BaseProcessor { public static String getId(String baseId) { return ResourcePrefix + baseId; } - private Logger logger = LoggerFactory.getLogger(this.getClass()); public List refreshIgMeasureContent(BaseProcessor parentContext, Encoding outputEncoding, Boolean versioned, FhirContext fhirContext, String measureToRefreshPath, Boolean shouldApplySoftwareSystemStamp) { return refreshIgMeasureContent(parentContext, outputEncoding, null, versioned, fhirContext, measureToRefreshPath, shouldApplySoftwareSystemStamp); } @@ -87,19 +87,19 @@ protected List getIdentifiers() { return identifiers; } - public void bundleMeasures(ArrayList refreshedLibraryNames, String igPath, List binaryPaths, Boolean includeDependencies, + public void bundleMeasures(List refreshedLibraryNames, String igPath, List binaryPaths, Boolean includeDependencies, Boolean includeTerminology, Boolean includePatientScenarios, Boolean includeVersion, Boolean addBundleTimestamp, FhirContext fhirContext, String fhirUri, Encoding encoding) { Map measures = IOUtils.getMeasures(fhirContext); //Map libraries = IOUtils.getLibraries(fhirContext); - List bundledMeasures = new ArrayList(); + List bundledMeasures = new ArrayList<>(); for (Map.Entry measureEntry : measures.entrySet()) { String measureSourcePath = IOUtils.getMeasurePathMap(fhirContext).get(measureEntry.getKey()); // Assumption - File name matches measure.name String measureName = FilenameUtils.getBaseName(measureSourcePath).replace(MeasureProcessor.ResourcePrefix, ""); try { - Map resources = new HashMap(); + Map resources = new HashMap<>(); Boolean shouldPersist = ResourceUtils.safeAddResource(measureSourcePath, resources, fhirContext); if (!resources.containsKey("Measure/" + measureEntry.getKey())) { @@ -115,14 +115,15 @@ public void bundleMeasures(ArrayList refreshedLibraryNames, String igPat primaryLibrary = IOUtils.getLibraries(fhirContext).get(primaryLibraryUrl); } - if (primaryLibrary == null) - throw new IllegalArgumentException(String.format("Could not resolve library url %s", primaryLibraryUrl)); + if (primaryLibrary == null) { + throw new IllegalArgumentException(String.format("Could not resolve library url %s", primaryLibraryUrl)); + } String primaryLibrarySourcePath = IOUtils.getLibraryPathMap(fhirContext).get(primaryLibrary.getIdElement().getIdPart()); String primaryLibraryName = ResourceUtils.getName(primaryLibrary, fhirContext); - if (includeVersion) { + if (Boolean.TRUE.equals(includeVersion)) { primaryLibraryName = primaryLibraryName + "-" + - fhirContext.newFhirPath().evaluateFirst(primaryLibrary, "version", IBase.class).get().toString(); + fhirContext.newFhirPath().evaluateFirst(primaryLibrary, "version", IBase.class).get(); } shouldPersist = shouldPersist @@ -136,34 +137,34 @@ public void bundleMeasures(ArrayList refreshedLibraryNames, String igPat throw new IllegalArgumentException(String.format("Could not determine CqlLibrarySource path for library %s", primaryLibraryName)); } - if (includeTerminology) { + if (Boolean.TRUE.equals(includeTerminology)) { boolean result = ValueSetsProcessor.bundleValueSets(cqlLibrarySourcePath, igPath, fhirContext, resources, encoding, includeDependencies, includeVersion); - if (shouldPersist && !result) { + if (Boolean.TRUE.equals(shouldPersist) && !result) { LogUtils.info("Measure will not be bundled because ValueSet bundling failed."); } shouldPersist = shouldPersist & result; } - if (includeDependencies) { + if (Boolean.TRUE.equals(includeDependencies)) { LibraryProcessor libraryProcessor = new LibraryProcessor(); boolean result = libraryProcessor.bundleLibraryDependencies(primaryLibrarySourcePath, fhirContext, resources, encoding, includeVersion); - if (shouldPersist && !result) { + if (Boolean.TRUE.equals(shouldPersist) && !result) { LogUtils.info("Measure will not be bundled because Library Dependency bundling failed."); } shouldPersist = shouldPersist & result; } - if (includePatientScenarios) { + if (Boolean.TRUE.equals(includePatientScenarios)) { boolean result = TestCaseProcessor.bundleTestCases(igPath, MeasureTestGroupName, primaryLibraryName, fhirContext, resources); - if (shouldPersist && !result) { + if (Boolean.TRUE.equals(shouldPersist) && !result) { LogUtils.info("Measure will not be bundled because Test Case bundling failed."); } shouldPersist = shouldPersist & result; } - if (shouldPersist) { + if (Boolean.TRUE.equals(shouldPersist)) { String bundleDestPath = FilenameUtils.concat(FilenameUtils.concat(IGProcessor.getBundlesPath(igPath), MeasureTestGroupName), measureName); - persistBundle(igPath, bundleDestPath, measureName, encoding, fhirContext, new ArrayList(resources.values()), fhirUri, addBundleTimestamp); + persistBundle(igPath, bundleDestPath, measureName, encoding, fhirContext, new ArrayList<>(resources.values()), fhirUri, addBundleTimestamp); bundleFiles(igPath, bundleDestPath, measureName, binaryPaths, measureSourcePath, primaryLibrarySourcePath, fhirContext, encoding, includeTerminology, includeDependencies, includePatientScenarios, includeVersion, addBundleTimestamp); bundledMeasures.add(measureSourcePath); } @@ -222,11 +223,11 @@ private void bundleFiles(String igPath, String bundleDestPath, String libraryNam String cqlDestPath = FilenameUtils.concat(bundleDestFilesPath, cqlFileName); IOUtils.copyFile(cqlLibrarySourcePath, cqlDestPath); - if (includeTerminology) { + if (Boolean.TRUE.equals(includeTerminology)) { try { Map valuesets = ResourceUtils.getDepValueSetResources(cqlLibrarySourcePath, igPath, fhirContext, includeDependencies, includeVersion); if (!valuesets.isEmpty()) { - Object bundle = BundleUtils.bundleArtifacts(ValueSetsProcessor.getId(libraryName), new ArrayList(valuesets.values()), fhirContext, addBundleTimestamp, this.getIdentifiers()); + Object bundle = BundleUtils.bundleArtifacts(ValueSetsProcessor.getId(libraryName), new ArrayList<>(valuesets.values()), fhirContext, addBundleTimestamp, this.getIdentifiers()); IOUtils.writeBundle(bundle, bundleDestFilesPath, encoding, fhirContext); } } catch (Exception e) { @@ -235,16 +236,16 @@ private void bundleFiles(String igPath, String bundleDestPath, String libraryNam } } - if (includeDependencies) { + if (Boolean.TRUE.equals(includeDependencies)) { Map depLibraries = ResourceUtils.getDepLibraryResources(librarySourcePath, fhirContext, encoding, includeVersion, logger); if (!depLibraries.isEmpty()) { String depLibrariesID = "library-deps-" + libraryName; - Object bundle = BundleUtils.bundleArtifacts(depLibrariesID, new ArrayList(depLibraries.values()), fhirContext, addBundleTimestamp, this.getIdentifiers()); + Object bundle = BundleUtils.bundleArtifacts(depLibrariesID, new ArrayList<>(depLibraries.values()), fhirContext, addBundleTimestamp, this.getIdentifiers()); IOUtils.writeBundle(bundle, bundleDestFilesPath, encoding, fhirContext); } } - if (includePatientScenarios) { + if (Boolean.TRUE.equals(includePatientScenarios)) { TestCaseProcessor.bundleTestCaseFiles(igPath, "measure", libraryName, bundleDestFilesPath, fhirContext); } } @@ -253,7 +254,7 @@ private void bundleFiles(String igPath, String bundleDestPath, String libraryNam protected FhirContext fhirContext; public List refreshMeasureContent(RefreshMeasureParameters params) { - return new ArrayList(); + return new ArrayList<>(); } protected List refreshGeneratedContent(List sourceMeasures) { @@ -262,7 +263,7 @@ protected List refreshGeneratedContent(List sourceMeasures) { private List internalRefreshGeneratedContent(List sourceMeasures) { // for each Measure, refresh the measure based on the primary measure library - List resources = new ArrayList(); + List resources = new ArrayList<>(); for (Measure measure : sourceMeasures) { resources.add(refreshGeneratedContent(measure)); } @@ -277,10 +278,10 @@ private Measure refreshGeneratedContent(Measure measure) { if (measure.hasLibrary()) { String libraryUrl = ResourceUtils.getPrimaryLibraryUrl(measure, fhirContext); VersionedIdentifier primaryLibraryIdentifier = CanonicalUtils.toVersionedIdentifier(libraryUrl); - List errors = new ArrayList(); - CompiledLibrary CompiledLibrary = libraryManager.resolveLibrary(primaryLibraryIdentifier, cqlTranslatorOptions, errors); + List errors = new ArrayList<>(); + CompiledLibrary compiledLibrary = libraryManager.resolveLibrary(primaryLibraryIdentifier, cqlTranslatorOptions, errors); boolean hasErrors = false; - if (errors.size() > 0) { + if (!errors.isEmpty()) { for (CqlCompilerException e : errors) { if (e.getSeverity() == CqlCompilerException.ErrorSeverity.Error) { hasErrors = true; @@ -289,7 +290,7 @@ private Measure refreshGeneratedContent(Measure measure) { } } if (!hasErrors) { - return processor.refreshMeasure(measure, libraryManager, CompiledLibrary, cqlTranslatorOptions); + return processor.refreshMeasure(measure, libraryManager, compiledLibrary, cqlTranslatorOptions); } } return measure; diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/npm/ILibraryReader.java b/tooling/src/main/java/org/opencds/cqf/tooling/npm/ILibraryReader.java deleted file mode 100644 index 792bf6858..000000000 --- a/tooling/src/main/java/org/opencds/cqf/tooling/npm/ILibraryReader.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.opencds.cqf.tooling.npm; - -import java.io.IOException; -import java.io.InputStream; - -import org.hl7.fhir.exceptions.FHIRFormatError; -import org.hl7.fhir.r5.model.Library; - -public interface ILibraryReader { - public Library readLibrary(InputStream stream) throws FHIRFormatError, IOException; -} diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/npm/LibraryLoader.java b/tooling/src/main/java/org/opencds/cqf/tooling/npm/LibraryLoader.java deleted file mode 100644 index 96bb72493..000000000 --- a/tooling/src/main/java/org/opencds/cqf/tooling/npm/LibraryLoader.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.opencds.cqf.tooling.npm; - -import java.io.IOException; -import java.io.InputStream; - -import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_14_50; -import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50; -import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_30_50; -import org.hl7.fhir.convertors.conv14_50.VersionConvertor_14_50; -import org.hl7.fhir.convertors.conv30_50.VersionConvertor_30_50; -import org.hl7.fhir.convertors.conv40_50.VersionConvertor_40_50; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.exceptions.FHIRFormatError; -import org.hl7.fhir.r5.formats.JsonParser; -import org.hl7.fhir.r5.model.Library; -import org.hl7.fhir.utilities.VersionUtilities; - -public class LibraryLoader implements ILibraryReader { - - private String version; - - public LibraryLoader(String version) { - this.version = version; - } - - @Override - public Library readLibrary(InputStream stream) throws FHIRFormatError, IOException { - if (VersionUtilities.isR2Ver(version)) { - throw new FHIRException("Library is not supported in R2"); - } else if (VersionUtilities.isR2BVer(version)) { - org.hl7.fhir.dstu2016may.model.Resource res = new org.hl7.fhir.dstu2016may.formats.JsonParser().parse(stream); - VersionConvertor_14_50 versionConvertor_14_50 = new VersionConvertor_14_50(new BaseAdvisor_14_50()); - return (Library) versionConvertor_14_50.convertResource(res); - } else if (VersionUtilities.isR3Ver(version)) { - org.hl7.fhir.dstu3.model.Resource res = new org.hl7.fhir.dstu3.formats.JsonParser().parse(stream); - VersionConvertor_30_50 versionConvertor_30_50 = new VersionConvertor_30_50(new BaseAdvisor_30_50()); - return (Library) versionConvertor_30_50.convertResource(res); - } else if (VersionUtilities.isR4Ver(version)) { - org.hl7.fhir.r4.model.Resource res = new org.hl7.fhir.r4.formats.JsonParser().parse(stream); - VersionConvertor_40_50 versionConvertor_40_50 = new VersionConvertor_40_50(new BaseAdvisor_40_50()); - return (Library) versionConvertor_40_50.convertResource(res); - } else if (VersionUtilities.isR5Ver(version)) { - return (Library) new JsonParser().parse(stream); - } else { - throw new FHIRException("Unknown Version '"+version+"'"); - } - } -} diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/npm/NpmLibrarySourceProvider.java b/tooling/src/main/java/org/opencds/cqf/tooling/npm/NpmLibrarySourceProvider.java deleted file mode 100644 index f7e797e8d..000000000 --- a/tooling/src/main/java/org/opencds/cqf/tooling/npm/NpmLibrarySourceProvider.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.opencds.cqf.tooling.npm; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.List; - -import org.cqframework.cql.cql2elm.LibrarySourceProvider; -import org.hl7.elm.r1.VersionedIdentifier; -import org.hl7.fhir.r5.context.IWorkerContext; -import org.hl7.fhir.r5.model.Library; -import org.hl7.fhir.utilities.npm.NpmPackage; - -/** - * Provides a library source provider that can resolve CQL library source from an Npm package - */ -public class NpmLibrarySourceProvider implements LibrarySourceProvider { - - public NpmLibrarySourceProvider(List packages, ILibraryReader reader, IWorkerContext.ILoggingService logger) { - this.packages = packages; - this.reader = reader; - this.logger = logger; - } - - private List packages; - private ILibraryReader reader; - private IWorkerContext.ILoggingService logger; - - @Override - public InputStream getLibrarySource(VersionedIdentifier identifier) { - // VersionedIdentifier.id: Name of the library - // VersionedIdentifier.system: Namespace for the library, as a URL - // VersionedIdentifier.version: Version of the library - - for (NpmPackage p : packages) { - try { - VersionedIdentifier libraryIdentifier = new VersionedIdentifier() - .withId(identifier.getId()) - .withVersion(identifier.getVersion()) - .withSystem(identifier.getSystem()); - - if (libraryIdentifier.getSystem() == null) { - libraryIdentifier.setSystem(p.canonical()); - } - - InputStream s = p.loadByCanonicalVersion(libraryIdentifier.getSystem()+"/Library/"+libraryIdentifier.getId(), libraryIdentifier.getVersion()); - if (s != null) { - Library l = reader.readLibrary(s); - for (org.hl7.fhir.r5.model.Attachment a : l.getContent()) { - if (a.getContentType() != null && a.getContentType().equals("text/cql")) { - if (identifier.getSystem() == null) { - identifier.setSystem(libraryIdentifier.getSystem()); - } - return new ByteArrayInputStream(a.getData()); - } - } - } - } catch (IOException e) { - logger.logDebugMessage(IWorkerContext.ILoggingService.LogCategory.PROGRESS, String.format("Exceptions occurred attempting to load npm library source for %s", identifier.toString())); - } - } - - return null; - } -} - diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/npm/NpmModelInfoProvider.java b/tooling/src/main/java/org/opencds/cqf/tooling/npm/NpmModelInfoProvider.java deleted file mode 100644 index c8ad6a2a1..000000000 --- a/tooling/src/main/java/org/opencds/cqf/tooling/npm/NpmModelInfoProvider.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.opencds.cqf.tooling.npm; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.List; - -import javax.xml.bind.JAXB; - -import org.hl7.cql.model.ModelIdentifier; -import org.hl7.cql.model.ModelInfoProvider; -import org.hl7.elm.r1.VersionedIdentifier; -import org.hl7.elm_modelinfo.r1.ModelInfo; -import org.hl7.fhir.r5.context.IWorkerContext; -import org.hl7.fhir.r5.model.Library; -import org.hl7.fhir.utilities.npm.NpmPackage; - -/** - * Provides a model info provider that can resolve CQL model info from an Npm package - */ -public class NpmModelInfoProvider implements ModelInfoProvider { - - public NpmModelInfoProvider(List packages, ILibraryReader reader, IWorkerContext.ILoggingService logger) { - this.packages = packages; - this.reader = reader; - this.logger = logger; - } - - private List packages; - private ILibraryReader reader; - private IWorkerContext.ILoggingService logger; - - public ModelInfo load(ModelIdentifier modelIdentifier) { - // VersionedIdentifier.id: Name of the model - // VersionedIdentifier.system: Namespace for the model, as a URL - // VersionedIdentifier.version: Version of the model - for (NpmPackage p : packages) { - try { - VersionedIdentifier identifier = new VersionedIdentifier() - .withId(modelIdentifier.getId()) - .withVersion(modelIdentifier.getVersion()) - .withSystem(modelIdentifier.getSystem()); - - if (identifier.getSystem() == null) { - identifier.setSystem(p.canonical()); - } - - InputStream s = p.loadByCanonicalVersion(identifier.getSystem()+"/Library/"+identifier.getId()+"-ModelInfo", identifier.getVersion()); - if (s != null) { - Library l = reader.readLibrary(s); - for (org.hl7.fhir.r5.model.Attachment a : l.getContent()) { - if (a.getContentType() != null && a.getContentType().equals("application/xml")) { - if (modelIdentifier.getSystem() == null) { - modelIdentifier.setSystem(identifier.getSystem()); - } - InputStream is = new ByteArrayInputStream(a.getData()); - return JAXB.unmarshal(is, ModelInfo.class); - } - } - } - } catch (IOException e) { - logger.logDebugMessage(IWorkerContext.ILoggingService.LogCategory.PROGRESS, String.format("Exceptions occurred attempting to load npm library for model %s", modelIdentifier.toString())); - } - } - - return null; - } -} - diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/npm/NpmPackageManager.java b/tooling/src/main/java/org/opencds/cqf/tooling/npm/NpmPackageManager.java deleted file mode 100644 index 140709048..000000000 --- a/tooling/src/main/java/org/opencds/cqf/tooling/npm/NpmPackageManager.java +++ /dev/null @@ -1,268 +0,0 @@ -package org.opencds.cqf.tooling.npm; - -import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLConnection; -import java.util.ArrayList; -import java.util.List; - -import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50; -import org.hl7.fhir.convertors.conv40_50.VersionConvertor_40_50; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.r4.formats.FormatUtilities; -import org.hl7.fhir.r5.model.Constants; -import org.hl7.fhir.r5.model.ImplementationGuide; -import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.VersionUtilities; -import org.hl7.fhir.utilities.json.JsonTrackingParser; -import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.npm.NpmPackage; -import org.hl7.fhir.utilities.npm.ToolsVersion; -import org.opencds.cqf.tooling.exception.NpmPackageManagerException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -public class NpmPackageManager { - - private static final Logger logger = LoggerFactory.getLogger(NpmPackageManager.class); - - private FilesystemPackageCacheManager pcm; - private List npmList = new ArrayList<>(); - - public List getNpmList() { - return npmList; - } - - private String version; // FHIR version as a string, e.g. 4.0.1 - - public String getVersion() { - return version; - } - - private ImplementationGuide sourceIg; - - public ImplementationGuide getSourceIg() { - return sourceIg; - } - - /* - * @param igPath Fully qualified path to the IG resource - */ - public static NpmPackageManager fromPath(String igPath, String version) throws IOException { - if (igPath == null || igPath.equals("")) { - throw new IllegalArgumentException("igPath is required"); - } - VersionConvertor_40_50 versionConvertor_40_50 = new VersionConvertor_40_50(new BaseAdvisor_40_50()); - return new NpmPackageManager( - (ImplementationGuide) versionConvertor_40_50.convertResource(FormatUtilities.loadFile(igPath)), - version); - } - - public static NpmPackageManager fromStream(InputStream is, String version) throws IOException { - VersionConvertor_40_50 versionConvertor_40_50 = new VersionConvertor_40_50(new BaseAdvisor_40_50()); - return new NpmPackageManager( - (ImplementationGuide) versionConvertor_40_50.convertResource(FormatUtilities.loadFile(is)), version); - } - - public NpmPackageManager(ImplementationGuide sourceIg, String version) { - if (version == null || version.equals("")) { - throw new IllegalArgumentException("version is required"); - } - - this.version = version; - - if (sourceIg == null) { - throw new IllegalArgumentException("sourceIg is required"); - } - - this.sourceIg = sourceIg; - - try { - // userMode indicates whether the packageCache is within the working directory - // or in the user home - pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION); - } catch (IOException e) { - throw new NpmPackageManagerException("error creating the FilesystemPackageCacheManager", e); - } - - loadCorePackage(); - - int i = 0; - for (ImplementationGuide.ImplementationGuideDependsOnComponent dep : sourceIg.getDependsOn()) { - try { - loadIg(dep, i); - i++; - } - catch(IOException e) { - throw new NpmPackageManagerException(String.format("Error loading IG dependency %s", dep.getId()), e); - } - } - } - - private void loadCorePackage() { - NpmPackage pi = null; - - String v = version.equals(Constants.VERSION) ? "current" : version; - - logger.info("Core Package {}#{}", VersionUtilities.packageForVersion(v), v); - try { - pi = pcm.loadPackage(VersionUtilities.packageForVersion(v), v); - } catch (Exception e) { - try { - logger.warn(String.format("First attempt at loading Core Package %s#%s failed", VersionUtilities.packageForVersion(v), v), e); - // Appears to be race condition in FHIR core where they are - // loading a custom cert provider. - pi = pcm.loadPackage(VersionUtilities.packageForVersion(v), v); - } catch (Exception ex) { - logger.error(String.format("Second attempt at loading Core Package %s#%s failed", VersionUtilities.packageForVersion(v), v), ex); - throw new NpmPackageManagerException("Error loading core package", ex); - } - } - - if (pi == null) { - throw new NpmPackageManagerException("Could not load core package"); - } - if (v.equals("current")) { - throw new IllegalArgumentException("Current core package not supported"); - } - npmList.add(pi); - } - - private void loadIg(ImplementationGuide.ImplementationGuideDependsOnComponent dep, int index) throws IOException { - String name = dep.getId(); - if (!dep.hasId()) { - logger.info("Dependency '{}' has no id, so can't be referred to in markdown in the IG", idForDep(dep)); - name = "u" + Utilities.makeUuidLC().replace("-", ""); - } - if (!isValidIGToken(name)) { - throw new IllegalArgumentException("IG Name must be a valid token (" + name + ")"); - } - - String canonical = determineCanonical(dep.getUri(), "ImplementationGuide.dependency[" + index + "].url"); - String packageId = dep.getPackageId(); - if (Utilities.noString(packageId)) - packageId = pcm.getPackageId(canonical); - if (Utilities.noString(canonical) && !Utilities.noString(packageId)) - canonical = pcm.getPackageUrl(packageId); - if (Utilities.noString(canonical)) - throw new IllegalArgumentException("You must specify a canonical URL for the IG " + name); - String igver = dep.getVersion(); - if (Utilities.noString(igver)) - throw new IllegalArgumentException( - "You must specify a version for the IG " + packageId + " (" + canonical + ")"); - - NpmPackage pi = packageId == null ? null : pcm.loadPackageFromCacheOnly(packageId, igver); - if (pi != null) - npmList.add(pi); - if (pi == null) { - pi = resolveDependency(canonical, packageId, igver); - if (pi == null) { - if (Utilities.noString(packageId)) - throw new IllegalArgumentException( - "Package Id for guide at " + canonical + " is unknown (contact FHIR Product Director"); - else - throw new IllegalArgumentException("Unknown Package " + packageId + "#" + igver); - } - } - - logger.debug( - "Load " + name + " (" + canonical + ") from " + packageId + "#" + igver); - - if (dep.hasUri() && !dep.getUri().contains("/ImplementationGuide/")) { - String cu = getIgUri(pi); - if (cu != null) { - logger.warn("The correct canonical URL for this dependency is " + cu); - } - } - } - - private String determineCanonical(String url, String path) throws FHIRException { - if (url == null) - return url; - if (url.contains("/ImplementationGuide/")) - return url.substring(0, url.indexOf("/ImplementationGuide/")); - if (path != null) { - logger.warn( - "The canonical URL for an Implementation Guide must point directly to the implementation guide resource, not to the Implementation Guide as a whole"); - } - return url; - } - - private boolean isValidIGToken(String tail) { - if (tail == null || tail.length() == 0) - return false; - boolean result = Utilities.isAlphabetic(tail.charAt(0)); - for (int i = 1; i < tail.length(); i++) { - result = result && (Utilities.isAlphabetic(tail.charAt(i)) || Utilities.isDigit(tail.charAt(i)) - || (tail.charAt(i) == '_')); - } - return result; - - } - - private String idForDep(ImplementationGuide.ImplementationGuideDependsOnComponent dep) { - if (dep.hasPackageId()) { - return dep.getPackageId(); - } - if (dep.hasUri()) { - return dep.getUri(); - } - return "{no id}"; - } - - private String getIgUri(NpmPackage pi) throws IOException { - for (String rs : pi.listResources("ImplementationGuide")) { - JsonObject json = JsonTrackingParser.parseJson(pi.loadResource(rs)); - if (json.has("packageId") && json.get("packageId").getAsString().equals(pi.name()) && json.has("url")) { - return json.get("url").getAsString(); - } - } - return null; - } - - private NpmPackage resolveDependency(String canonical, String packageId, String igver) throws IOException { - if (packageId != null) - return pcm.loadPackage(packageId, igver); - - JsonObject pl; - logger.debug("Fetch Package history from " + Utilities.pathURL(canonical, "package-list.json")); - - try { - pl = fetchJson(Utilities.pathURL(canonical, "package-list.json")); - } catch (Exception e) { - return null; - } - if (!canonical.equals(pl.get("canonical").getAsString())) - throw new IllegalArgumentException("Canonical mismatch fetching package list for " + canonical + "#" + igver - + ", package-list.json says " + pl.get("canonical")); - for (JsonElement e : pl.getAsJsonArray("list")) { - JsonObject o = (JsonObject) e; - if (igver.equals(o.get("version").getAsString())) { - InputStream src = fetchFromSource(pl.get("package-id").getAsString() + "-" + igver, - Utilities.pathURL(o.get("path").getAsString(), "package.tgz")); - return pcm.addPackageToCache(pl.get("package-id").getAsString(), igver, src, - Utilities.pathURL(o.get("path").getAsString(), "package.tgz")); - } - } - return null; - } - - private JsonObject fetchJson(String source) throws IOException { - URL url = new URL(source + "?nocache=" + System.currentTimeMillis()); - HttpURLConnection c = (HttpURLConnection) url.openConnection(); - c.setInstanceFollowRedirects(true); - return JsonTrackingParser.parseJson(c.getInputStream()); - } - - private InputStream fetchFromSource(String id, String source) throws IOException { - logger.debug("Fetch " + id + " package from " + source); - URL url = new URL(source + "?nocache=" + System.currentTimeMillis()); - URLConnection c = url.openConnection(); - return c.getInputStream(); - } -} diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/operation/IgBundler.java b/tooling/src/main/java/org/opencds/cqf/tooling/operation/IgBundler.java index 2c7b128e4..d7e36cac8 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/operation/IgBundler.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/operation/IgBundler.java @@ -1,16 +1,3 @@ - - - - - - - - - - - - - package org.opencds.cqf.tooling.operation; import java.io.File; diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/operation/LocalValueSetExpand.java b/tooling/src/main/java/org/opencds/cqf/tooling/operation/LocalValueSetExpand.java new file mode 100644 index 000000000..56ce129e8 --- /dev/null +++ b/tooling/src/main/java/org/opencds/cqf/tooling/operation/LocalValueSetExpand.java @@ -0,0 +1,129 @@ +package org.opencds.cqf.tooling.operation; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.util.BundleUtil; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.ValueSet; +import org.opencds.cqf.tooling.Operation; +import org.opencds.cqf.tooling.utilities.BundleUtils; +import org.opencds.cqf.tooling.utilities.IOUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class LocalValueSetExpand extends Operation { + private String encoding; // -encoding (-e) + private String pathToDirectory; // -pathtodir (-ptd) + private String version; // -version (-v) Can be dstu2, stu3, or r4 + private FhirContext fhirContext; + + @Override + public void execute(String[] args) { + setOutputPath("src/main/resources/org/opencds/cqf/tooling/terminology/output"); + for (String arg : args) { + if (arg.equals("-LocalVSExpand")) continue; + String[] flagAndValue = arg.split("="); + if (flagAndValue.length < 2) { + throw new IllegalArgumentException("Invalid argument: " + arg); + } + String flag = flagAndValue[0]; + String value = flagAndValue[1]; + + switch (flag.replace("-", "").toLowerCase()) { + case "encoding": + case "e": + encoding = value.toLowerCase(); + break; + case "outputpath": + case "op": + setOutputPath(value); + break; // -outputpath (-op) + case "pathtodir": + case "ptd": + pathToDirectory = value; + break; + case "version": case "v": + version = value; + break; + default: throw new IllegalArgumentException("Unknown flag: " + flag); + } + } + + if (encoding == null || encoding.isEmpty()) { + encoding = "json"; + } else { + if (!encoding.equalsIgnoreCase("xml") && !encoding.equalsIgnoreCase("json")) { + throw new IllegalArgumentException(String.format("Unsupported encoding: %s. Allowed encodings { json, xml }", encoding)); + } + } + + if (pathToDirectory == null) { + pathToDirectory = "/Users/christopherschuler/Documents/workspace/cqframework/igs/opioid-cds-r4/input/vocabulary/valueset"; + } + + if (version == null) { + fhirContext = FhirContext.forR4Cached(); + } + else { + switch (version.toLowerCase()) { + case "dstu2": + fhirContext = FhirContext.forDstu2Cached(); + break; + case "stu3": + fhirContext = FhirContext.forDstu3Cached(); + break; + case "r4": + fhirContext = FhirContext.forR4Cached(); + break; + default: + throw new IllegalArgumentException("Unknown fhir version: " + version); + } + } + + Class clazz = + fhirContext.getResourceDefinition("ValueSet").newInstance().getClass(); + IBaseBundle bundle = BundleUtils.getBundleOfResourceTypeFromDirectory(pathToDirectory, fhirContext, clazz); + List valueSets = BundleUtil.toListOfResources(fhirContext, bundle); + Map valueSetMap = new HashMap<>(); + valueSets.forEach(valueSet -> valueSetMap.put(((ValueSet) valueSet).getUrl(), valueSet)); + + ValueSet allScreenVS = (ValueSet) IOUtils.readResource( + "/Users/christopherschuler/Documents/workspace/cqframework/igs/opioid-cds-r4/input/vocabulary/valueset/all-screenings.json", fhirContext); + ValueSet allMedVS = (ValueSet) IOUtils.readResource( + "/Users/christopherschuler/Documents/workspace/cqframework/igs/opioid-cds-r4/input/vocabulary/valueset/all-medications.json", fhirContext); + + List expansion = new ArrayList<>(); + allScreenVS.getCompose().getInclude().forEach( + include -> { + if (include.hasValueSet()) { + String vsUrl = include.getValueSet().get(0).asStringValue(); + expansion.addAll(((ValueSet) valueSetMap.get(vsUrl)).getExpansion().getContains()); + } + } + ); + Set codeSet = new HashSet<>(); + allScreenVS.setExpansion(new ValueSet.ValueSetExpansionComponent().setContains( + expansion.stream().filter(e -> codeSet.add(e.getCode())).collect(Collectors.toList()))); + + allMedVS.getCompose().getInclude().forEach( + include -> { + if (include.hasValueSet()) { + String vsUrl = include.getValueSet().get(0).asStringValue(); + expansion.addAll(((ValueSet) valueSetMap.get(vsUrl)).getExpansion().getContains()); + } + } + ); + allMedVS.setExpansion(new ValueSet.ValueSetExpansionComponent().setContains( + expansion.stream().filter(e -> codeSet.add(e.getCode())).collect(Collectors.toList()))); + + IOUtils.writeResource(allScreenVS, getOutputPath(), IOUtils.Encoding.JSON, fhirContext); + IOUtils.writeResource(allMedVS, getOutputPath(), IOUtils.Encoding.JSON, fhirContext); + } + +} diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/operation/PostBundlesInDirOperation.java b/tooling/src/main/java/org/opencds/cqf/tooling/operation/PostBundlesInDirOperation.java index fa9a6fb53..a1a141623 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/operation/PostBundlesInDirOperation.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/operation/PostBundlesInDirOperation.java @@ -20,6 +20,6 @@ public void execute(String[] args) { System.err.println(e.getMessage()); System.exit(1); } - PostBundlesInDirProcessor.PostBundlesInDir(params); + PostBundlesInDirProcessor.postBundlesInDir(params); } } \ No newline at end of file diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/operation/RefreshIGOperation.java b/tooling/src/main/java/org/opencds/cqf/tooling/operation/RefreshIGOperation.java index 42cf36e87..169ad1ae3 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/operation/RefreshIGOperation.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/operation/RefreshIGOperation.java @@ -13,16 +13,13 @@ public class RefreshIGOperation extends Operation { - public RefreshIGOperation() { - } - @Override public void execute(String[] args) { - - if (args == null) { - throw new IllegalArgumentException(); - } - + + if (args == null) { + throw new IllegalArgumentException(); + } + RefreshIGParameters params = null; try { params = new RefreshIGArgumentProcessor().parseAndConvert(args); diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/operation/RefreshLibraryOperation.java b/tooling/src/main/java/org/opencds/cqf/tooling/operation/RefreshLibraryOperation.java index 821045e5f..75e9f5f5f 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/operation/RefreshLibraryOperation.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/operation/RefreshLibraryOperation.java @@ -9,14 +9,11 @@ import org.opencds.cqf.tooling.utilities.IOUtils; import org.opencds.cqf.tooling.utilities.LogUtils; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; public class RefreshLibraryOperation extends Operation { - public RefreshLibraryOperation() { - } - @Override public void execute(String[] args) { RefreshLibraryParameters params = null; @@ -28,7 +25,7 @@ public void execute(String[] args) { System.exit(1); } - List refreshedLibraryNames = new ArrayList(); + List refreshedLibraryNames; LibraryProcessor libraryProcessor; switch (params.fhirContext.getVersion().getVersion()) { @@ -45,7 +42,7 @@ public void execute(String[] args) { "Unknown fhir version: " + params.fhirContext.getVersion().getVersion().getFhirVersionString()); } - if (refreshedLibraryNames.size() == 0) { + if (refreshedLibraryNames.isEmpty()) { LogUtils.info("No libraries successfully refreshed."); LogUtils.warn(params.cqlContentPath); } @@ -59,19 +56,15 @@ public void execute(String[] args) { public static List refreshLibraryContent(RefreshLibraryParameters params, LibraryProcessor libraryProcessor) { try { if(params.libraryPath.isEmpty()) { - try { - params.libraryPath = IOUtils.getLibraryPathAssociatedWithCqlFileName(params.cqlContentPath, params.fhirContext); - } catch (Exception e) { - LogUtils.putException(params.cqlContentPath, e); - LogUtils.warn(params.cqlContentPath); - } + params.libraryPath = IOUtils.getLibraryPathAssociatedWithCqlFileName(params.cqlContentPath, params.fhirContext); } return libraryProcessor.refreshLibraryContent(params); } catch (Exception e) { LogUtils.putException(params.cqlContentPath, e); + LogUtils.warn(params.cqlContentPath); } LogUtils.warn(params.cqlContentPath); - return null; + return Collections.emptyList(); } } diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/IGInfo.java b/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/IGInfo.java new file mode 100644 index 000000000..a8619f2a5 --- /dev/null +++ b/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/IGInfo.java @@ -0,0 +1,350 @@ +package org.opencds.cqf.tooling.operation.ig; + +import ca.uhn.fhir.context.FhirContext; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; +import org.cqframework.fhir.utilities.exception.IGInitializationException; +import org.hl7.fhir.r5.model.ImplementationGuide; +import org.opencds.cqf.tooling.utilities.IOUtils; +import org.opencds.cqf.tooling.utilities.converters.ResourceAndTypeConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class IGInfo { + private static final Logger logger = LoggerFactory.getLogger(IGInfo.class); + private final FhirContext fhirContext; + private final String rootDir; + private final String iniPath; + private final String igPath; + private final String cqlBinaryPath; + private final String resourcePath; + private final String libraryResourcePath; + private boolean refreshLibraries = true; + private final String planDefinitionResourcePath; + private boolean refreshPlanDefinitions = true; + private final String measureResourcePath; + private boolean refreshMeasures = true; + private final String valueSetResourcePath; + private final String codeSystemResourcePath; + private final String activityDefinitionResourcePath; + private final String questionnaireResourcePath; + private final ImplementationGuide igResource; + private final String packageId; + private final String canonical; + + public IGInfo(FhirContext fhirContext, String rootDir) { + if (fhirContext == null) { + this.fhirContext = FhirContext.forR4Cached(); + logger.info("The FHIR context was not provided, using {}", + this.fhirContext.getVersion().getVersion().getFhirVersionString()); + } + else { + this.fhirContext = fhirContext; + } + if (rootDir == null) { + throw new IGInitializationException("The root directory path for the IG not provided"); + } + this.rootDir = rootDir; + this.iniPath = getIniPath(); + this.igPath = getIgPath(); + this.cqlBinaryPath = getCqlBinaryPath(); + this.resourcePath = getResourcePath(); + this.libraryResourcePath = getLibraryResourcePath(); + this.planDefinitionResourcePath = getPlanDefinitionResourcePath(); + this.measureResourcePath = getMeasureResourcePath(); + this.valueSetResourcePath = getValueSetResourcePath(); + this.codeSystemResourcePath = getCodeSystemResourcePath(); + this.activityDefinitionResourcePath = getActivityDefinitionResourcePath(); + this.questionnaireResourcePath = getQuestionnaireResourcePath(); + this.igResource = getIgResource(); + this.packageId = getPackageId(); + this.canonical = getCanonical(); + } + + public FhirContext getFhirContext() { + return fhirContext; + } + + public String getRootDir() { + return rootDir; + } + + public String getIniPath() { + if (this.iniPath != null) { + return this.iniPath; + } + try (Stream walk = Files.walk(Paths.get(this.rootDir))) { + List pathList = walk.filter(p -> !Files.isDirectory(p)) + .map(p -> p.toString().toLowerCase()) + .filter(f -> f.endsWith("ig.ini")) + .collect(Collectors.toList()); + if (pathList.isEmpty()) { + logger.error("Unable to determine path to IG ini file"); + throw new IGInitializationException("An IG ini file must be present! See https://build.fhir.org/ig/FHIR/ig-guidance/using-templates.html#igroot for more information."); + } + else if (pathList.size() > 1) { + logger.warn("Found multiple IG ini files, using {}", pathList.get(0)); + } + return pathList.get(0); + } catch (IOException ioe) { + logger.error("Error determining path to IG ini file"); + throw new IGInitializationException(ioe.getMessage(), ioe); + } + } + + public String getIgPath() { + if (this.igPath != null) { + return this.igPath; + } + try { + List igList = FileUtils.readLines(new File(iniPath), StandardCharsets.UTF_8) + .stream().filter(s -> s.startsWith("ig")).map( + s -> StringUtils.deleteWhitespace(s).replace("ig=", "")) + .collect(Collectors.toList()); + if (igList.isEmpty()) { + logger.error("Unable to determine path to IG resource file"); + throw new IGInitializationException("An IG resource file must be present! See https://build.fhir.org/ig/FHIR/ig-guidance/using-templates.html#igroot-input for more information."); + } + else if (igList.size() > 1) { + logger.warn("Found multiple IG resource files, using {}", igList.get(0)); + } + return FilenameUtils.concat(rootDir, igList.get(0)); + } catch (IOException ioe) { + logger.error("Error determining path to IG resource file"); + throw new IGInitializationException(ioe.getMessage(), ioe); + } + } + + public String getCqlBinaryPath() { + if (this.cqlBinaryPath != null) { + return this.cqlBinaryPath; + } + // preferred directory structure + String candidate = FilenameUtils.concat(getRootDir(), "input/cql"); + if (new File(candidate).isDirectory()) { + return candidate; + } + // support legacy directory structure + candidate = FilenameUtils.concat(getRootDir(), "input/pagecontent/cql"); + if (new File(candidate).isDirectory()) { + return candidate; + } else { + String message = "Unable to locate CQL binary directory, Please see https://github.com/cqframework/sample-content-ig#directory-structure for guidance on content IG directory structure."; + logger.error(message); + throw new IGInitializationException(message); + } + } + + public String getResourcePath() { + if (this.resourcePath != null) { + return this.resourcePath; + } + String candidate = FilenameUtils.concat(getRootDir(), "input/resources"); + if (new File(candidate).isDirectory()) { + return candidate; + } else { + String message = "Unable to locate the resources directory, Please see https://github.com/cqframework/sample-content-ig#directory-structure for guidance on content IG directory structure."; + logger.error(message); + throw new IGInitializationException(message); + } + } + + public String getLibraryResourcePath() { + if (this.libraryResourcePath != null) { + return this.libraryResourcePath; + } + if (refreshLibraries) { + String candidate = FilenameUtils.concat(getResourcePath(), "library"); + if (new File(candidate).isDirectory()) { + return candidate; + } else { + logger.warn("Unable to locate the Library resource directory. The base resources path will be used."); + return getResourcePath(); + } + } + return null; + } + + public boolean isRefreshLibraries() { + return this.refreshLibraries; + } + + public void setRefreshLibraries(boolean refreshLibraries) { + this.refreshLibraries = refreshLibraries; + } + + public String getPlanDefinitionResourcePath() { + if (this.planDefinitionResourcePath != null) { + return this.planDefinitionResourcePath; + } + if (refreshPlanDefinitions) { + String candidate = FilenameUtils.concat(getResourcePath(), "plandefinition"); + if (new File(candidate).isDirectory()) { + return candidate; + } else { + logger.warn("Unable to locate the PlanDefinition resource directory. The base resources path will be used."); + return getResourcePath(); + } + } + return null; + } + + public boolean isRefreshPlanDefinitions() { + return this.refreshPlanDefinitions; + } + + public void setRefreshPlanDefinitions(boolean refreshPlanDefinitions) { + this.refreshPlanDefinitions = refreshPlanDefinitions; + } + + public String getMeasureResourcePath() { + if (this.measureResourcePath != null) { + return this.measureResourcePath; + } + if (refreshPlanDefinitions) { + String candidate = FilenameUtils.concat(getResourcePath(), "measure"); + if (new File(candidate).isDirectory()) { + return candidate; + } else { + logger.warn("Unable to locate the Measure resource directory. The base resources path will be used."); + return getResourcePath(); + } + } + return null; + } + + public boolean isRefreshMeasures() { + return this.refreshMeasures; + } + + public void setRefreshMeasures(boolean refreshMeasures) { + this.refreshMeasures = refreshMeasures; + } + + public String getValueSetResourcePath() { + if (this.valueSetResourcePath != null) { + return this.valueSetResourcePath; + } + String candidate = FilenameUtils.concat(getResourcePath(), "vocabulary/valueset"); + if (new File(candidate).isDirectory()) { + return candidate; + } + candidate = FilenameUtils.concat(getResourcePath(), "vocabulary"); + if (new File(candidate).isDirectory()) { + return candidate; + } + candidate = FilenameUtils.concat(getRootDir(), "input/vocabulary/valueset"); + if (new File(candidate).isDirectory()) { + return candidate; + } + candidate = FilenameUtils.concat(getRootDir(), "input/vocabulary"); + if (new File(candidate).isDirectory()) { + return candidate; + } else { + logger.warn("Unable to locate the ValueSet resource directory. The base resources path will be used."); + return getResourcePath(); + } + } + + public String getCodeSystemResourcePath() { + if (this.codeSystemResourcePath != null) { + return this.codeSystemResourcePath; + } + String candidate = FilenameUtils.concat(getResourcePath(), "vocabulary/codesystem"); + if (new File(candidate).isDirectory()) { + return candidate; + } + candidate = FilenameUtils.concat(getResourcePath(), "codesystem"); + if (new File(candidate).isDirectory()) { + return candidate; + } + candidate = FilenameUtils.concat(getRootDir(), "input/vocabulary/codesystem"); + if (new File(candidate).isDirectory()) { + return candidate; + } + candidate = FilenameUtils.concat(getRootDir(), "input/codesystem"); + if (new File(candidate).isDirectory()) { + return candidate; + } else { + logger.warn("Unable to locate the CodeSystem resource directory. The base resources path will be used."); + return getResourcePath(); + } + } + + public String getActivityDefinitionResourcePath() { + if (this.activityDefinitionResourcePath != null) { + return this.activityDefinitionResourcePath; + } + String candidate = FilenameUtils.concat(getResourcePath(), "activitydefinition"); + if (new File(candidate).isDirectory()) { + return candidate; + } else { + logger.warn("Unable to locate the ActivityDefinition resource directory. The base resources path will be used."); + return getResourcePath(); + } + } + + public String getQuestionnaireResourcePath() { + if (this.questionnaireResourcePath != null) { + return this.questionnaireResourcePath; + } + String candidate = FilenameUtils.concat(getResourcePath(), "questionnaire"); + if (new File(candidate).isDirectory()) { + return candidate; + } else { + logger.warn("Unable to locate the Questionnaire resource directory. The base resources path will be used."); + return getResourcePath(); + } + } + + public ImplementationGuide getIgResource() { + if (this.igResource != null) { + return this.igResource; + } + switch (this.fhirContext.getVersion().getVersion()) { + case DSTU3: + return (ImplementationGuide) ResourceAndTypeConverter.stu3ToR5Resource(IOUtils.readResource(igPath, this.fhirContext)); + case R4: + return (ImplementationGuide) ResourceAndTypeConverter.r4ToR5Resource(IOUtils.readResource(igPath, this.fhirContext)); + case R5: return (ImplementationGuide) IOUtils.readResource(igPath, this.fhirContext); + default: throw new IGInitializationException( + "Unsupported FHIR context: " + this.fhirContext.getVersion().getVersion().getFhirVersionString()); + } + } + + public String getPackageId() { + if (this.packageId != null) { + return this.packageId; + } + if (!getIgResource().hasPackageId()) { + String message = "A package ID must be present in the IG resource"; + logger.error(message); + throw new IGInitializationException(message); + } + return getIgResource().getPackageId(); + } + + public String getCanonical() { + if (this.canonical != null) { + return this.canonical; + } + if (!getIgResource().hasUrl()) { + String message = "A canonical must be present in the IG resource"; + logger.error(message); + throw new IGInitializationException(message); + } + String url = getIgResource().getUrl(); + return url.contains("/ImplementationGuide/") ? url.substring(0, url.indexOf("/ImplementationGuide/")) : url; + } +} diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/IGLoggingService.java b/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/IGLoggingService.java new file mode 100644 index 000000000..a97fcb553 --- /dev/null +++ b/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/IGLoggingService.java @@ -0,0 +1,24 @@ +package org.opencds.cqf.tooling.operation.ig; + +import org.hl7.fhir.r5.context.IWorkerContext; +import org.slf4j.Logger; + +public class IGLoggingService implements IWorkerContext.ILoggingService { + + private final Logger logger; + + public IGLoggingService(Logger logger) { + this.logger = logger; + } + + @Override + public void logMessage(String s) { + logger.info(s); + } + + @Override + public void logDebugMessage(LogCategory logCategory, String message) { + String category = logCategory.name(); + logger.debug("Category: {} Message: {}", category, message); + } +} diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/LibraryPackage.java b/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/LibraryPackage.java new file mode 100644 index 000000000..38571c27a --- /dev/null +++ b/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/LibraryPackage.java @@ -0,0 +1,89 @@ +package org.opencds.cqf.tooling.operation.ig; + +import ca.uhn.fhir.context.FhirContext; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.opencds.cqf.tooling.processor.CqlProcessor; +import org.opencds.cqf.tooling.utilities.ResourceUtils; + +import java.util.ArrayList; +import java.util.List; + +public class LibraryPackage { + private final IBaseResource library; + private final FhirContext fhirContext; + private CqlProcessor.CqlSourceFileInformation cqlFileInfo; + private List dependsOnLibraries; + private List dependsOnValueSets; + private List dependsOnCodeSystems; + + public LibraryPackage(IBaseResource library, FhirContext fhirContext, CqlProcessor.CqlSourceFileInformation cqlFileInfo) { + this.library = library; + this.fhirContext = fhirContext; + this.cqlFileInfo = cqlFileInfo; + this.dependsOnLibraries = new ArrayList<>(); + this.dependsOnValueSets = new ArrayList<>(); + this.dependsOnCodeSystems = new ArrayList<>(); + } + + public IBaseResource getLibrary() { + return library; + } + + public List getDependsOnLibraries() { + return dependsOnLibraries; + } + + public void addDependsOnLibrary(IBaseResource library) { + if (library != null && this.dependsOnLibraries.stream().noneMatch( + dep -> ResourceUtils.compareResourceIdUrlAndVersion(library, dep, fhirContext))) { + this.dependsOnLibraries.add(library); + } + } + + public void setDependsOnLibraries(List dependsOnLibraries) { + this.dependsOnLibraries = dependsOnLibraries; + } + + public List getDependsOnValueSets() { + return dependsOnValueSets; + } + + public void addDependsOnValueSet(IBaseResource valueSet) { + if (valueSet != null && this.dependsOnValueSets.stream().noneMatch( + dep -> ResourceUtils.compareResourceIdUrlAndVersion(valueSet, dep, fhirContext))) { + this.dependsOnValueSets.add(valueSet); + } + } + + public void setDependsOnValueSets(List dependsOnValueSets) { + this.dependsOnValueSets = dependsOnValueSets; + } + + public List getDependsOnCodeSystems() { + return dependsOnCodeSystems; + } + + public void addDependsOnCodeSystem(IBaseResource codeSystem) { + // TODO: CodeSystems are extensible... Possible for multiple with the same ID - currently just including the first + if (codeSystem != null && this.dependsOnCodeSystems.stream().noneMatch( + dep -> ResourceUtils.compareResourcePrimitiveElements(codeSystem, dep, fhirContext, "id"))) { + this.dependsOnCodeSystems.add(codeSystem); + } + } + + public void setDependsOnCodeSystems(List dependsOnCodeSystems) { + this.dependsOnCodeSystems = dependsOnCodeSystems; + } + + public CqlProcessor.CqlSourceFileInformation getCqlFileInfo() { + return cqlFileInfo; + } + + public void setCqlFileInfo(CqlProcessor.CqlSourceFileInformation cqlFileInfo) { + this.cqlFileInfo = cqlFileInfo; + } + + public FhirContext getFhirContext() { + return fhirContext; + } +} diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/LibraryRefresh.java b/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/LibraryRefresh.java new file mode 100644 index 000000000..c8d6ea28d --- /dev/null +++ b/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/LibraryRefresh.java @@ -0,0 +1,260 @@ +package org.opencds.cqf.tooling.operation.ig; + +import ca.uhn.fhir.util.TerserUtil; +import ca.uhn.fhir.util.UrlUtil; +import org.apache.commons.io.FilenameUtils; +import org.cqframework.fhir.npm.LibraryLoader; +import org.cqframework.fhir.npm.NpmPackageManager; +import org.cqframework.fhir.utilities.exception.IGInitializationException; +import org.fhir.ucum.UcumEssenceService; +import org.fhir.ucum.UcumException; +import org.hl7.elm.r1.VersionedIdentifier; +import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.model.Attachment; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.opencds.cqf.tooling.processor.CqlProcessor; +import org.opencds.cqf.tooling.utilities.CanonicalUtils; +import org.opencds.cqf.tooling.utilities.IOUtils; +import org.opencds.cqf.tooling.utilities.ResourceUtils; +import org.opencds.cqf.tooling.utilities.converters.ResourceAndTypeConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +public class LibraryRefresh extends Refresh { + private static final Logger logger = LoggerFactory.getLogger(LibraryRefresh.class); + private final CqlProcessor cqlProcessor; + private final NpmPackageManager npmPackageManager; + private final List libraryPackages; + + public LibraryRefresh(IGInfo igInfo) { + super(igInfo); + this.npmPackageManager = new NpmPackageManager(igInfo.getIgResource()); + this.libraryPackages = new ArrayList<>(); + LibraryLoader libraryLoader = new LibraryLoader(igInfo.getFhirContext().getVersion().getVersion().getFhirVersionString()); + UcumEssenceService ucumService; + try { + ucumService = new UcumEssenceService(UcumEssenceService.class.getResourceAsStream("/ucum-essence.xml")); + } catch (UcumException e) { + throw new IGInitializationException("Could not create UCUM validation service", e); + } + this.cqlProcessor = new CqlProcessor(cleanPackageList(this.npmPackageManager.getNpmList()), + Collections.singletonList(igInfo.getCqlBinaryPath()), libraryLoader, new IGLoggingService(logger), ucumService, + igInfo.getPackageId(), igInfo.getCanonical()); + } + + @Override + public List refresh() { + List refreshedLibraries = new ArrayList<>(); + this.cqlProcessor.execute(); + if (getIgInfo().isRefreshLibraries()) { + logger.info("Refreshing Libraries..."); + + for (var library : getResourcesOfTypeFromDirectory("Library", getIgInfo().getLibraryResourcePath())) { + String name = ResourceUtils.getName(library, getFhirContext()); + + logger.info("Refreshing {}", library.getIdElement()); + + for (CqlProcessor.CqlSourceFileInformation info : cqlProcessor.getAllFileInformation()) { + if (info.getIdentifier().getId().endsWith(name)) { + // TODO: should likely verify or resolve/refresh the following elements: + // cpg-knowledgeCapability, cpg-knowledgeRepresentationLevel, url, identifier, status, + // experimental, type, publisher, contact, description, useContext, jurisdiction, + // and profile(s) (http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-shareablelibrary) + refreshDate(library); + refreshContent(library, info); + refreshDataRequirements(library, info); + refreshRelatedArtifacts(library, info); + refreshParameters(library, info); + refreshedLibraries.add(library); + this.libraryPackages.add(new LibraryPackage(library, getFhirContext(), info)); + } + } + + logger.info("Success!"); + } + resolveLibraryPackages(); + } + return refreshedLibraries; + } + + private void resolveLibraryPackages() { + // See the comment below regarding terminology resolution below + List sourceIGValueSets = + getResourcesOfTypeFromDirectory("ValueSet", getIgInfo().getValueSetResourcePath()); + List sourceIGCodeSystems = + getResourcesOfTypeFromDirectory("CodeSystem", getIgInfo().getCodeSystemResourcePath()); + this.libraryPackages.forEach( + libraryPackage -> { + libraryPackage.setDependsOnValueSets(sourceIGValueSets); + libraryPackage.setDependsOnCodeSystems(sourceIGCodeSystems); + libraryPackage.getCqlFileInfo().getRelatedArtifacts().forEach( + relatedArtifact -> { + if (relatedArtifact.hasResource() && UrlUtil.isValid(relatedArtifact.getResource())) { + VersionedIdentifier identifier; + if (relatedArtifact.getResource().contains("/Library/")) { + identifier = CanonicalUtils.toVersionedIdentifier(relatedArtifact.getResource()); + if (identifier.getSystem().equals(getIgInfo().getCanonical())) { + // retrieve from existing packages (source IG) + libraryPackage.addDependsOnLibrary(getLibraryPackage(identifier).getLibrary()); + } + else { + // retrieve from local NPM packages + libraryPackage.addDependsOnLibrary(getLibraryFromNpmPackage(identifier)); + } + } + // TODO: resolve terminology from source IG - currently just including all terminology + // due to some limitations in data requirements processing + else if (relatedArtifact.getResource().contains("/ValueSet/")) { + identifier = CanonicalUtils.toVersionedIdentifierAnyResource(relatedArtifact.getResource()); + if (!identifier.getSystem().equals(getIgInfo().getCanonical())) { + libraryPackage.addDependsOnValueSet(getValueSetFromNpmPackage(identifier)); + } + } + else if (relatedArtifact.getResource().contains("/CodeSystem/")) { + identifier = CanonicalUtils.toVersionedIdentifierAnyResource(relatedArtifact.getResource()); + if (!identifier.getSystem().equals(getIgInfo().getCanonical())) { + libraryPackage.addDependsOnCodeSystem(getCodeSystemFromNpmPackage(identifier)); + } + } + } + } + ); + } + ); + } + + private LibraryPackage getLibraryPackage(VersionedIdentifier identifier) { + return this.libraryPackages.stream().filter( + pkg -> pkg.getCqlFileInfo().getIdentifier().equals(identifier)).findFirst().orElse(null); + } + + private LibraryPackage getLibraryPackage(String url) { + return this.libraryPackages.stream().filter( + pkg -> url.endsWith(pkg.getLibrary().getIdElement().getIdPart())).findFirst().orElse(null); + } + + Map> npmPackageLibraryCache = new HashMap<>(); + private IBaseResource getLibraryFromNpmPackage(VersionedIdentifier identifier) { + return getResourceFromNpmPackage(identifier, "Library", npmPackageLibraryCache); + } + + Map> npmPackageValueSetCache = new HashMap<>(); + private IBaseResource getValueSetFromNpmPackage(VersionedIdentifier identifier) { + return getResourceFromNpmPackage(identifier, "ValueSet", npmPackageValueSetCache); + } + + Map> npmPackageCodeSystemCache = new HashMap<>(); + private IBaseResource getCodeSystemFromNpmPackage(VersionedIdentifier identifier) { + return getResourceFromNpmPackage(identifier, "CodeSystem", npmPackageCodeSystemCache); + } + + private IBaseResource getResourceFromNpmPackage(VersionedIdentifier identifier, String resourceType, + Map> resourceCache) { + String url; + if ((resourceType.equals("ValueSet") || resourceType.equals("CodeSystem")) + && identifier.getSystem().equals("http://terminology.hl7.org")) { + url = "http://hl7.org/fhir"; + } + else { + url = identifier.getSystem(); + } + if (!resourceCache.containsKey(url)) { + NpmPackage npmPackage = getNpmPackage(url); + if (npmPackage != null && npmPackage.getFolders().containsKey("package") ) { + String path = FilenameUtils.concat(npmPackage.getPath(), "package"); + if (npmPackage.getFolders().get("package").getTypes().containsKey(resourceType)) { + resourceCache.put(url, + npmPackage.getFolders().get("package").getTypes().get(resourceType).stream().map( + fileName -> IOUtils.readJsonResourceIgnoreElements( + FilenameUtils.concat(path, fileName), getFhirContext(), "text")) + .collect(Collectors.toList())); + } + } + } + if (resourceCache.containsKey(url)) { + return resourceCache.get(url).stream().filter( + resource -> { + VersionedIdentifier cachedIdentifier = ResourceUtils.getIdentifier(resource, getFhirContext()); + if (identifier.getVersion() == null) { + // non-versioned urls - typically for terminology resources + cachedIdentifier.setVersion(null); + } + return cachedIdentifier.equals(identifier); + }).findFirst().orElse(null); + } + logger.warn("Could not resolve {} from local packages", identifier); + return null; + } + + private NpmPackage getNpmPackage(String url) { + Optional npmPackage = this.npmPackageManager.getNpmList().stream() + .filter(pkg -> pkg.getNpm().has("canonical") + && pkg.getNpm().getAsJsonPrimitive("canonical").getAsString().equals(url)) + .findFirst(); + if (npmPackage.isPresent()) { + logger.warn("Could not resolve canonical url {} from local packages", url); + } + return npmPackage.orElse(null); + } + + // TODO: move this deduplication logic to the translator + private List cleanPackageList(List originalPackageList) { + Set pathSet = new HashSet<>(); + return originalPackageList.stream().filter(e -> pathSet.add(e.getPath())) + .collect(Collectors.toList()); + } + + private void refreshContent(IBaseResource library, CqlProcessor.CqlSourceFileInformation info) { + Attachment cql = new Attachment().setContentType("text/cql").setData(info.getCql()); + Attachment elmXml = new Attachment().setContentType("application/elm+xml").setData(info.getElm()); + Attachment elmJson = new Attachment().setContentType("application/elm+json").setData(info.getJsonElm()); + TerserUtil.clearField(getFhirContext(), library, "content"); + TerserUtil.setField(getFhirContext(), "content", library, + ResourceAndTypeConverter.convertType(getFhirContext(), cql), + ResourceAndTypeConverter.convertType(getFhirContext(), elmXml), + ResourceAndTypeConverter.convertType(getFhirContext(), elmJson)); + } + + private void refreshDataRequirements(IBaseResource library, CqlProcessor.CqlSourceFileInformation info) { + IBase[] dataRequirements = info.getDataRequirements().stream() + .map(dataRequirement -> ResourceAndTypeConverter.convertType(getFhirContext(), dataRequirement)) + .toArray(IBase[]::new); + TerserUtil.clearField(getFhirContext(), library, "dataRequirement"); + TerserUtil.setField(getFhirContext(), "dataRequirement", library, dataRequirements); + } + + private void refreshRelatedArtifacts(IBaseResource library, CqlProcessor.CqlSourceFileInformation info) { + IBase[] relatedArtifacts = info.getRelatedArtifacts().stream() + .map(relatedArtifact -> ResourceAndTypeConverter.convertType(getFhirContext(), relatedArtifact)) + .toArray(IBase[]::new); + TerserUtil.clearField(getFhirContext(), library, "relatedArtifact"); + TerserUtil.setField(getFhirContext(), "relatedArtifact", library, relatedArtifacts); + } + + private void refreshParameters(IBaseResource library, CqlProcessor.CqlSourceFileInformation info) { + IBase[] parameters = info.getParameters().stream() + .map(parameter -> ResourceAndTypeConverter.convertType(getFhirContext(), parameter)) + .toArray(IBase[]::new); + TerserUtil.clearField(getFhirContext(), library, "parameter"); + TerserUtil.setField(getFhirContext(), "parameter", library, parameters); + } + + public CqlProcessor getCqlProcessor() { + return this.cqlProcessor; + } + + public List getLibraryPackages() { + return libraryPackages; + } +} diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/MeasureRefresh.java b/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/MeasureRefresh.java new file mode 100644 index 000000000..db45f3218 --- /dev/null +++ b/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/MeasureRefresh.java @@ -0,0 +1,110 @@ +package org.opencds.cqf.tooling.operation.ig; + +import ca.uhn.fhir.util.BundleUtil; +import org.cqframework.cql.elm.requirements.fhir.DataRequirementsProcessor; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.model.Library; +import org.hl7.fhir.r5.model.Measure; +import org.opencds.cqf.tooling.processor.CqlProcessor; +import org.opencds.cqf.tooling.utilities.BundleUtils; +import org.opencds.cqf.tooling.utilities.constants.CqfmConstants; +import org.opencds.cqf.tooling.utilities.converters.ResourceAndTypeConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class MeasureRefresh extends Refresh { + private static final Logger logger = LoggerFactory.getLogger(MeasureRefresh.class); + private final CqlProcessor cqlProcessor; + + public MeasureRefresh(IGInfo igInfo, CqlProcessor cqlProcessor) { + super(igInfo); + this.cqlProcessor = cqlProcessor; + } + + @Override + public List refresh() { + List refreshedMeasures = new ArrayList<>(); + + if (getIgInfo().isRefreshMeasures()) { + logger.info("Refreshing Measures..."); + + if (cqlProcessor.getFileMap() == null) { + cqlProcessor.execute(); + } + + DataRequirementsProcessor dataRecProc = new DataRequirementsProcessor(); + Class clazz = getFhirContext().getResourceDefinition( + "Measure").newInstance().getClass(); + IBaseBundle bundle = BundleUtils.getBundleOfResourceTypeFromDirectory( + getIgInfo().getMeasureResourcePath(), getFhirContext(), clazz); + + for (var resource : BundleUtil.toListOfResources(getFhirContext(), bundle)) { + Measure measure = (Measure) ResourceAndTypeConverter.convertToR5Resource(getFhirContext(), resource); + + logger.info("Refreshing {}", measure.getId()); + + validatePrimaryLibraryReference(measure); + String libraryUrl = measure.getLibrary().get(0).getValueAsString(); + for (CqlProcessor.CqlSourceFileInformation info : cqlProcessor.getAllFileInformation()) { + if (libraryUrl.endsWith(info.getIdentifier().getId())) { + // TODO: should likely verify or resolve/refresh the following elements: + // cqfm-artifactComment, cqfm-allocation, cqfm-softwaresystem, url, identifier, version, + // name, title, status, experimental, type, publisher, contact, description, useContext, + // jurisdiction, and profile(s) (http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/measure-cqfm) + measure.setDate(new Date()); + addProfiles(measure, CqfmConstants.COMPUTABLE_MEASURE_PROFILE_URL); + Library moduleDefinitionLibrary = getModuleDefinitionLibrary( + measure, dataRecProc, info); + refreshCqfmExtensions(measure, moduleDefinitionLibrary); + attachModuleDefinitionLibrary(measure, moduleDefinitionLibrary); + refreshedMeasures.add( + ResourceAndTypeConverter.convertFromR5Resource(getFhirContext(), measure)); + } + } + + logger.info("Success!"); + } + } + return refreshedMeasures; + } + + private Library getModuleDefinitionLibrary(Measure measure, DataRequirementsProcessor dataRecProc, + CqlProcessor.CqlSourceFileInformation info) { + Set expressions = getExpressions(measure); + return dataRecProc.gatherDataRequirements( + cqlProcessor.getLibraryManager(), + cqlProcessor.getLibraryManager().resolveLibrary( + info.getIdentifier(), info.getOptions(), new ArrayList<>()), + info.getOptions(), expressions, true); + } + + private Set getExpressions(Measure measure) { + Set expressionSet = new HashSet<>(); + // TODO: check if expression is a cql expression + measure.getSupplementalData().forEach(supData -> { + if (supData.hasCriteria() && isExpressionIdentifier(supData.getCriteria())) { + expressionSet.add(supData.getCriteria().getExpression()); + } + }); + measure.getGroup().forEach(groupMember -> { + groupMember.getPopulation().forEach(population -> { + if (population.hasCriteria() && isExpressionIdentifier(population.getCriteria())) { + expressionSet.add(population.getCriteria().getExpression()); + } + }); + groupMember.getStratifier().forEach(stratifier -> { + if (stratifier.hasCriteria() && isExpressionIdentifier(stratifier.getCriteria())) { + expressionSet.add(stratifier.getCriteria().getExpression()); + } + }); + }); + return expressionSet; + } +} diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/NewRefreshIGOperation.java b/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/NewRefreshIGOperation.java new file mode 100644 index 000000000..3c64ea4e7 --- /dev/null +++ b/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/NewRefreshIGOperation.java @@ -0,0 +1,127 @@ +package org.opencds.cqf.tooling.operation.ig; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.util.BundleBuilder; +import org.apache.commons.io.FilenameUtils; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.opencds.cqf.tooling.Operation; +import org.opencds.cqf.tooling.parameter.RefreshIGParameters; +import org.opencds.cqf.tooling.processor.argument.RefreshIGArgumentProcessor; +import org.opencds.cqf.tooling.utilities.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.List; + +public class NewRefreshIGOperation extends Operation { + private static final Logger logger = LoggerFactory.getLogger(NewRefreshIGOperation.class); + private RefreshIGParameters params; + + @Override + public void execute(String[] args) { + try { + this.params = new RefreshIGArgumentProcessor().parseAndConvert(args); + IGInfo info = new IGInfo(null, params.rootDir); + LibraryRefresh libraryRefresh = new LibraryRefresh(info); + publishLibraries(info, libraryRefresh.refresh()); + PlanDefinitionRefresh planDefinitionRefresh = new PlanDefinitionRefresh(info, libraryRefresh.getCqlProcessor(), libraryRefresh.getLibraryPackages()); + publishPlanDefinitions(info, planDefinitionRefresh.refresh()); + publishPlanDefinitionBundles(planDefinitionRefresh); + MeasureRefresh measureRefresh = new MeasureRefresh(info, libraryRefresh.getCqlProcessor()); + publishMeasures(info, measureRefresh.refresh()); + // TODO: bundle IG/testcases + } catch (Exception e) { + logger.error(e.getMessage()); + System.exit(1); + } + } + + private void publishPlanDefinitionBundles(PlanDefinitionRefresh planDefinitionRefresh) { + String pathToBundles = FilenameUtils.concat(params.rootDir, "bundles"); + String pathToPlanDefinitionBundles = FilenameUtils.concat(pathToBundles, "plandefinition"); + try { + IOUtils.ensurePath(pathToBundles); + IOUtils.ensurePath(pathToPlanDefinitionBundles); + planDefinitionRefresh.getPlanDefinitionPackages().forEach( + pkg -> { + String id = pkg.getPlanDefinition().getIdElement().getIdPart(); + String pathToPackage = FilenameUtils.concat(pathToPlanDefinitionBundles, id); + IOUtils.writeResource(pkg.bundleResources(), pathToPackage, IOUtils.Encoding.JSON, + pkg.getFhirContext(), params.versioned, id + "-bundle"); + String pathToFiles = FilenameUtils.concat(pathToPackage, "files"); + IOUtils.writeResource(pkg.getPlanDefinition(), pathToFiles, IOUtils.Encoding.JSON, + pkg.getFhirContext()); + id = pkg.getLibraryPackage().getLibrary().getIdElement().getIdPart(); + IOUtils.writeResource(pkg.getLibraryPackage().getLibrary(), pathToFiles, IOUtils.Encoding.JSON, + pkg.getFhirContext()); + BundleBuilder builder = new BundleBuilder(pkg.getFhirContext()); + pkg.getLibraryPackage().getDependsOnLibraries().forEach(builder::addTransactionUpdateEntry); + IOUtils.writeResource(builder.getBundle(), pathToFiles, IOUtils.Encoding.JSON, pkg.getFhirContext(), + params.versioned, "library-deps-" + id + "-bundle"); + builder = new BundleBuilder(pkg.getFhirContext()); + pkg.getLibraryPackage().getDependsOnValueSets().forEach(builder::addTransactionUpdateEntry); + pkg.getLibraryPackage().getDependsOnCodeSystems().forEach(builder::addTransactionUpdateEntry); + IOUtils.writeResource(builder.getBundle(), pathToFiles, IOUtils.Encoding.JSON, pkg.getFhirContext(), + params.versioned, "terminology-" + id + "-bundle"); + // TODO: output CQL and ELM - also maybe XML files? + } + ); + } catch (IOException ioe) { + logger.warn(ioe.getMessage()); + } + } + + private void publishLibraries (IGInfo igInfo, List libraries) { + String outputPath = this.params.libraryOutputPath != null && !this.params.libraryOutputPath.isEmpty() + ? this.params.libraryOutputPath : igInfo.getLibraryResourcePath(); + for (var library : libraries) { + applySoftwareSystemStamp(igInfo.getFhirContext(), library); + IOUtils.writeResource(library, outputPath, this.params.outputEncoding, + igInfo.getFhirContext(), this.params.versioned, true); + } + } + + private void publishPlanDefinitions (IGInfo igInfo, List planDefinitions) { + // TODO: enable user to set output path + String outputPath = igInfo.getPlanDefinitionResourcePath(); + for (var planDefinition : planDefinitions) { + applySoftwareSystemStamp(igInfo.getFhirContext(), planDefinition); + IOUtils.writeResource(planDefinition, outputPath, this.params.outputEncoding, + igInfo.getFhirContext(), this.params.versioned, true); + } + } + + private void publishMeasures (IGInfo igInfo, List measures) { + String outputPath = this.params.measureOutputPath != null && !this.params.measureOutputPath.isEmpty() + ? this.params.measureOutputPath : igInfo.getMeasureResourcePath(); + for (var measure : measures) { + applySoftwareSystemStamp(igInfo.getFhirContext(), measure); + IOUtils.writeResource(measure, outputPath, this.params.outputEncoding, + igInfo.getFhirContext(), this.params.versioned, true); + } + } + + private org.opencds.cqf.tooling.common.r4.CqfmSoftwareSystemHelper r4CqfmSoftwareSystemHelper; + private org.opencds.cqf.tooling.common.stu3.CqfmSoftwareSystemHelper dstu3CqfmSoftwareSystemHelper; + private void applySoftwareSystemStamp (FhirContext fhirContext, IBaseResource resource) { + if (Boolean.TRUE.equals(this.params.shouldApplySoftwareSystemStamp)) { + if (resource instanceof org.hl7.fhir.r4.model.DomainResource) { + if (r4CqfmSoftwareSystemHelper == null) { + r4CqfmSoftwareSystemHelper = new org.opencds.cqf.tooling.common.r4.CqfmSoftwareSystemHelper(); + } + r4CqfmSoftwareSystemHelper.ensureCQFToolingExtensionAndDevice( + (org.hl7.fhir.r4.model.DomainResource) resource, fhirContext); + } else if (resource instanceof org.hl7.fhir.dstu3.model.DomainResource) { + if (dstu3CqfmSoftwareSystemHelper == null) { + dstu3CqfmSoftwareSystemHelper = new org.opencds.cqf.tooling.common.stu3.CqfmSoftwareSystemHelper(); + } + dstu3CqfmSoftwareSystemHelper.ensureCQFToolingExtensionAndDevice( + (org.hl7.fhir.dstu3.model.DomainResource) resource, fhirContext); + } else { + logger.warn("CqfmSoftwareSystemHelper not supported for version {}", + fhirContext.getVersion().getVersion().getFhirVersionString()); + } + } + } +} diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/PlanDefinitionPackage.java b/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/PlanDefinitionPackage.java new file mode 100644 index 000000000..2de82a8fc --- /dev/null +++ b/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/PlanDefinitionPackage.java @@ -0,0 +1,107 @@ +package org.opencds.cqf.tooling.operation.ig; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.util.BundleBuilder; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.model.PlanDefinition; +import org.opencds.cqf.tooling.utilities.ResourceUtils; + +import java.util.ArrayList; +import java.util.List; + +public class PlanDefinitionPackage { + private final PlanDefinition r5PlanDefinition; // used for packaging + private final IBaseResource planDefinition; + private final FhirContext fhirContext; + private final LibraryPackage libraryPackage; + private List activityDefinitions; + private List questionnaires; + + // TODO: handle nested PlanDefinitions + private List nestedPlanDefinitions; + + public PlanDefinitionPackage(PlanDefinition r5PlanDefinition, IBaseResource planDefinition, + FhirContext fhirContext, LibraryPackage libraryPackage) { + this.r5PlanDefinition = r5PlanDefinition; + this.planDefinition = planDefinition; + this.fhirContext = fhirContext; + this.libraryPackage = libraryPackage; + this.activityDefinitions = new ArrayList<>(); + this.questionnaires = new ArrayList<>(); + } + + public IBaseBundle bundleResources() { + BundleBuilder builder = new BundleBuilder(this.fhirContext); + builder.addTransactionUpdateEntry(planDefinition); + builder.addTransactionUpdateEntry(libraryPackage.getLibrary()); + libraryPackage.getDependsOnLibraries().forEach(builder::addTransactionUpdateEntry); + libraryPackage.getDependsOnValueSets().forEach(builder::addTransactionUpdateEntry); + libraryPackage.getDependsOnCodeSystems().forEach(builder::addTransactionUpdateEntry); + activityDefinitions.forEach(builder::addTransactionUpdateEntry); + questionnaires.forEach(builder::addTransactionUpdateEntry); + return builder.getBundle(); + } + + public PlanDefinition getR5PlanDefinition() { + return r5PlanDefinition; + } + + public IBaseResource getPlanDefinition() { + return planDefinition; + } + + public FhirContext getFhirContext() { + return fhirContext; + } + + public LibraryPackage getLibraryPackage() { + return libraryPackage; + } + + public List getActivityDefinitions() { + return activityDefinitions; + } + + public void addActivityDefinition(IBaseResource activityDefinition) { + if (activityDefinition != null && this.activityDefinitions.stream().noneMatch( + dep -> ResourceUtils.compareResourceIdUrlAndVersion(activityDefinition, dep, fhirContext))) { + this.activityDefinitions.add(activityDefinition); + } + } + + public void setActivityDefinitions(List activityDefinitions) { + this.activityDefinitions = activityDefinitions; + } + + public List getQuestionnaires() { + return questionnaires; + } + + public void addQuestionnaire(IBaseResource questionnaire) { + if (questionnaire != null && this.questionnaires.stream().noneMatch( + dep -> ResourceUtils.compareResourceIdUrlAndVersion(questionnaire, dep, fhirContext))) { + this.questionnaires.add(questionnaire); + } + } + + public void setQuestionnaires(List questionnaires) { + this.questionnaires = questionnaires; + } + + public List getNestedPlanDefinitions() { + return nestedPlanDefinitions; + } + + public void addNestedPlanDefinition(IBaseResource planDefinition) { + if (planDefinition != null && this.nestedPlanDefinitions.stream().noneMatch( + dep -> ResourceUtils.compareResourceIdUrlAndVersion( + planDefinition, dep.getPlanDefinition(), fhirContext))) { + this.questionnaires.add(planDefinition); + } + } + + public void setNestedPlanDefinitions(List nestedPlanDefinitions) { + this.nestedPlanDefinitions = nestedPlanDefinitions; + } +} diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/PlanDefinitionRefresh.java b/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/PlanDefinitionRefresh.java new file mode 100644 index 000000000..f0ca152ed --- /dev/null +++ b/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/PlanDefinitionRefresh.java @@ -0,0 +1,185 @@ +package org.opencds.cqf.tooling.operation.ig; + +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.util.BundleUtil; +import org.cqframework.cql.elm.requirements.fhir.DataRequirementsProcessor; +import org.cqframework.fhir.npm.NpmPackageManager; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.model.Enumerations; +import org.hl7.fhir.r5.model.Library; +import org.hl7.fhir.r5.model.PlanDefinition; +import org.opencds.cqf.tooling.processor.CqlProcessor; +import org.opencds.cqf.tooling.utilities.BundleUtils; +import org.opencds.cqf.tooling.utilities.converters.ResourceAndTypeConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class PlanDefinitionRefresh extends Refresh { + private static final Logger logger = LoggerFactory.getLogger(PlanDefinitionRefresh.class); + private final CqlProcessor cqlProcessor; + private final List libraryPackages; + private final List planDefinitionPackages; + + public PlanDefinitionRefresh(IGInfo igInfo, CqlProcessor cqlProcessor, List libraryPackages) { + super(igInfo); + this.cqlProcessor = cqlProcessor; + this.libraryPackages = libraryPackages; + this.planDefinitionPackages = new ArrayList<>(); + } + + @Override + public List refresh() { + List refreshedPlanDefinitions = new ArrayList<>(); + + if (getIgInfo().isRefreshPlanDefinitions()) { + logger.info("Refreshing PlanDefinitions..."); + + if (cqlProcessor.getFileMap() == null) { + cqlProcessor.execute(); + } + + DataRequirementsProcessor dataRecProc = new DataRequirementsProcessor(); + Class clazz = getFhirContext().getResourceDefinition( + "PlanDefinition").newInstance().getClass(); + IBaseBundle bundle = BundleUtils.getBundleOfResourceTypeFromDirectory( + getIgInfo().getPlanDefinitionResourcePath(), getFhirContext(), clazz); + + for (var resource : BundleUtil.toListOfResources(getFhirContext(), bundle)) { + PlanDefinition planDefinition = (PlanDefinition) ResourceAndTypeConverter.convertToR5Resource( + getFhirContext(), resource); + + logger.info("Refreshing {}", planDefinition.getId()); + + validatePrimaryLibraryReference(planDefinition); + String libraryUrl = planDefinition.getLibrary().get(0).getValueAsString(); + LibraryPackage libraryPackage = libraryPackages.stream().filter( + pkg -> libraryUrl.endsWith(pkg.getCqlFileInfo().getIdentifier().getId())) + .findFirst().orElse(null); + for (CqlProcessor.CqlSourceFileInformation info : cqlProcessor.getAllFileInformation()) { + if (libraryUrl.endsWith(info.getIdentifier().getId())) { + // TODO: should likely verify or resolve/refresh the following elements: + // cpg-knowledgeCapability, cpg-knowledgeRepresentationLevel, url, identifier, status, + // experimental, type, publisher, contact, description, useContext, jurisdiction, + // and profile(s) (http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-shareableplandefinition) + planDefinition.setDate(new Date()); + Library moduleDefinitionLibrary = getModuleDefinitionLibrary( + planDefinition, dataRecProc, info); + cleanModuleDefinitionLibrary(moduleDefinitionLibrary); + refreshCqfmExtensions(planDefinition, moduleDefinitionLibrary); + attachModuleDefinitionLibrary(planDefinition, moduleDefinitionLibrary); + IBaseResource refreshedPlanDefinition = ResourceAndTypeConverter.convertFromR5Resource(getFhirContext(), planDefinition); + refreshedPlanDefinitions.add(refreshedPlanDefinition); + this.planDefinitionPackages.add(new PlanDefinitionPackage(planDefinition, refreshedPlanDefinition, getFhirContext(), libraryPackage)); + } + } + + logger.info("Success!"); + } + resolvePlanDefinitionPackages(); + } + return refreshedPlanDefinitions; + } + + private List activityDefinitions; + private List questionnaires; + private void resolvePlanDefinitionPackages() { + // TODO: only resolving definition resources from source IG - enhance to resolve from NPM package. + // Additionally need to resolve nested PlanDefinitions + activityDefinitions = BundleUtil.toListOfResources(getFhirContext(), + BundleUtils.getBundleOfResourceTypeFromDirectory(getIgInfo().getActivityDefinitionResourcePath(), + getFhirContext(), getFhirContext().getResourceDefinition("ActivityDefinition") + .newInstance().getClass())); + questionnaires = BundleUtil.toListOfResources(getFhirContext(), + BundleUtils.getBundleOfResourceTypeFromDirectory(getIgInfo().getActivityDefinitionResourcePath(), + getFhirContext(), getFhirContext().getResourceDefinition("Questionnaire") + .newInstance().getClass())); + this.planDefinitionPackages.forEach( + pkg -> pkg.getR5PlanDefinition().getAction().forEach(action -> resolveAction(action, pkg)) + ); + } + + private void resolveAction(PlanDefinition.PlanDefinitionActionComponent action, PlanDefinitionPackage pkg) { + final IdDt definitionRef; + if (action.hasDefinitionCanonicalType()) { + definitionRef = new IdDt(action.getDefinitionCanonicalType().getValueAsString()); + } + else if (action.hasDefinitionUriType()) { + definitionRef = new IdDt(action.getDefinitionUriType().getValueAsString()); + } + else { + definitionRef = null; + } + if (definitionRef != null && definitionRef.hasResourceType()) { + if (definitionRef.getResourceType().equals("ActivityDefinition")) { + pkg.addActivityDefinition( + activityDefinitions.stream().filter(ad -> ad.getIdElement().getIdPart() + .equals(definitionRef.getIdPart())).findFirst().orElse(null) + ); + + } + else if (definitionRef.getResourceType().equals("Questionnaire")) { + pkg.addQuestionnaire( + questionnaires.stream().filter(q -> q.getIdElement().getIdPart() + .equals(definitionRef.getIdPart())).findFirst().orElse(null) + ); + } + else { + logger.warn("Definitions of type {} are not currently supported", definitionRef.getResourceType()); + } + } + if (action.hasAction()) { + action.getAction().forEach(nextAction -> resolveAction(nextAction, pkg)); + } + } + + public void bundleResources(NpmPackageManager npmPackageManager, List resources) { + + } + + private Library getModuleDefinitionLibrary(PlanDefinition planDefinition, DataRequirementsProcessor dataRecProc, + CqlProcessor.CqlSourceFileInformation info) { + Set expressions = new HashSet<>(); + if (planDefinition.hasAction()) { + getExpressions(planDefinition.getAction(), expressions); + } + return dataRecProc.gatherDataRequirements( + cqlProcessor.getLibraryManager(), + cqlProcessor.getLibraryManager().resolveLibrary( + info.getIdentifier(), info.getOptions().withAnalyzeDataRequirements(true), new ArrayList<>()), + info.getOptions(), expressions, true); + } + + private void getExpressions(List actions, Set expressions) { + for (var action : actions) { + if (action.hasCondition()) { + for (var condition : action.getCondition()) { + if (condition.hasKind() && condition.getKind() == Enumerations.ActionConditionKind.APPLICABILITY + && condition.hasExpression() && isExpressionIdentifier(condition.getExpression())) { + expressions.add(condition.getExpression().getExpression()); + } + } + } + if (action.hasDynamicValue()) { + for (var dynamicValue : action.getDynamicValue()) { + if (dynamicValue.hasExpression() && isExpressionIdentifier(dynamicValue.getExpression())) { + expressions.add(dynamicValue.getExpression().getExpression()); + } + } + } + if (action.hasAction()) { + getExpressions(action.getAction(), expressions); + } + } + } + + public List getPlanDefinitionPackages() { + return planDefinitionPackages; + } +} diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/Refresh.java b/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/Refresh.java new file mode 100644 index 000000000..9337da11f --- /dev/null +++ b/tooling/src/main/java/org/opencds/cqf/tooling/operation/ig/Refresh.java @@ -0,0 +1,118 @@ +package org.opencds.cqf.tooling.operation.ig; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.util.BundleUtil; +import ca.uhn.fhir.util.TerserUtil; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.model.DateTimeType; +import org.hl7.fhir.r5.model.Expression; +import org.hl7.fhir.r5.model.Extension; +import org.hl7.fhir.r5.model.Library; +import org.hl7.fhir.r5.model.Measure; +import org.hl7.fhir.r5.model.Meta; +import org.hl7.fhir.r5.model.MetadataResource; +import org.hl7.fhir.r5.model.PlanDefinition; +import org.hl7.fhir.r5.model.Reference; +import org.opencds.cqf.tooling.utilities.BundleUtils; +import org.opencds.cqf.tooling.utilities.constants.CqfmConstants; +import org.opencds.cqf.tooling.utilities.converters.ResourceAndTypeConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public abstract class Refresh { + private static final Logger logger = LoggerFactory.getLogger(Refresh.class); + private final IGInfo igInfo; + private final FhirContext fhirContext; + + public Refresh(IGInfo igInfo) { + this.igInfo = igInfo; + this.fhirContext = igInfo.getFhirContext(); + } + + public abstract List refresh(); + + public void refreshDate(IBaseResource resource) { + TerserUtil.setField(getIgInfo().getFhirContext(), "date", resource, + ResourceAndTypeConverter.convertType(getFhirContext(), new DateTimeType(new Date()))); + } + + public void validatePrimaryLibraryReference(IBaseResource resource) { + if ((resource instanceof PlanDefinition && !((PlanDefinition) resource).hasLibrary()) + || (resource instanceof Measure && !((Measure) resource).hasLibrary())) { + String message = resource.fhirType() + " resources must have a Library reference"; + logger.error(message); + throw new FHIRException(message); + } + } + + public boolean isExpressionIdentifier(Expression expression) { + return expression.hasLanguage() && expression.hasExpression() + && (expression.getLanguage().equalsIgnoreCase("text/cql.identifier") + || expression.getLanguage().equalsIgnoreCase("text/cql")); + } + + public void refreshCqfmExtensions(MetadataResource resource, Library moduleDefinitionLibrary) { + resource.getExtension().removeAll(resource.getExtensionsByUrl(CqfmConstants.PARAMETERS_EXT_URL)); + resource.getExtension().removeAll(resource.getExtensionsByUrl(CqfmConstants.DATA_REQUIREMENT_EXT_URL)); + resource.getExtension().removeAll(resource.getExtensionsByUrl(CqfmConstants.DIRECT_REF_CODE_EXT_URL)); + resource.getExtension().removeAll(resource.getExtensionsByUrl(CqfmConstants.LOGIC_DEFINITION_EXT_URL)); + resource.getExtension().removeAll(resource.getExtensionsByUrl(CqfmConstants.EFFECTIVE_DATA_REQS_EXT_URL)); + + for (Extension extension : moduleDefinitionLibrary.getExtension()) { + if (extension.hasUrl() && extension.getUrl().equals(CqfmConstants.DIRECT_REF_CODE_EXT_URL)) { + continue; + } + resource.addExtension(extension); + } + } + + public void attachModuleDefinitionLibrary(MetadataResource resource, Library moduleDefinitionLibrary) { + String effectiveDataReq = "effective-data-requirements"; + resource.getContained().removeIf( + res -> res.getId().equalsIgnoreCase("#" + effectiveDataReq)); + moduleDefinitionLibrary.setExtension(Collections.emptyList()); + resource.addContained(moduleDefinitionLibrary.setId(effectiveDataReq)); + resource.addExtension() + .setUrl(CqfmConstants.EFFECTIVE_DATA_REQS_EXT_URL) + .setValue(new Reference("#" + effectiveDataReq)).setId(effectiveDataReq); + } + + public void addProfiles(MetadataResource resource, String... profiles) { + if (!resource.hasMeta()) { + resource.setMeta(new Meta()); + } + Arrays.stream(profiles).filter(profile -> !resource.getMeta().hasProfile(profile)) + .forEach(profile -> resource.getMeta().addProfile(profile)); + } + + public void cleanModuleDefinitionLibrary(Library moduleDefinitionLibrary) { + Set pathSet = new HashSet<>(); + moduleDefinitionLibrary.setRelatedArtifact(moduleDefinitionLibrary.getRelatedArtifact().stream() + .filter(e -> pathSet.add(e.getResource())).collect(Collectors.toList())); + } + + public List getResourcesOfTypeFromDirectory(String resourceType, String directoryPath) { + Class clazz = + getFhirContext().getResourceDefinition(resourceType).newInstance().getClass(); + IBaseBundle bundle = BundleUtils.getBundleOfResourceTypeFromDirectory(directoryPath, getFhirContext(), clazz); + return BundleUtil.toListOfResources(getFhirContext(), bundle); + } + + public IGInfo getIgInfo() { + return igInfo; + } + + public FhirContext getFhirContext() { + return fhirContext; + } +} diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/parameter/RefreshIGParameters.java b/tooling/src/main/java/org/opencds/cqf/tooling/parameter/RefreshIGParameters.java index 20676c06c..9c114233c 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/parameter/RefreshIGParameters.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/parameter/RefreshIGParameters.java @@ -1,6 +1,6 @@ package org.opencds.cqf.tooling.parameter; -import java.util.ArrayList; +import java.util.List; import org.opencds.cqf.tooling.utilities.IOUtils; @@ -17,7 +17,7 @@ public class RefreshIGParameters { public Boolean shouldApplySoftwareSystemStamp; public Boolean addBundleTimestamp; public String fhirUri; - public ArrayList resourceDirs; + public List resourceDirs; public Boolean conformant; public String measureToRefreshPath; public String libraryOutputPath; diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/processor/BaseProcessor.java b/tooling/src/main/java/org/opencds/cqf/tooling/processor/BaseProcessor.java index d028610ed..3ea86078e 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/processor/BaseProcessor.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/processor/BaseProcessor.java @@ -4,6 +4,8 @@ import java.io.IOException; import java.util.List; +import org.cqframework.fhir.npm.LibraryLoader; +import org.cqframework.fhir.npm.NpmPackageManager; import org.fhir.ucum.UcumEssenceService; import org.fhir.ucum.UcumException; import org.fhir.ucum.UcumService; @@ -19,9 +21,7 @@ import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; -import org.opencds.cqf.tooling.exception.IGInitializationException; -import org.opencds.cqf.tooling.npm.LibraryLoader; -import org.opencds.cqf.tooling.npm.NpmPackageManager; +import org.cqframework.fhir.utilities.exception.IGInitializationException; import org.opencds.cqf.tooling.utilities.IGUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -114,7 +114,7 @@ public void initializeFromIg(String rootDir, String igPath, String fhirVersion) this.fhirVersion = sourceIg.getFhirVersion().get(0).getCode(); packageId = sourceIg.getPackageId(); canonicalBase = determineCanonical(sourceIg.getUrl()); - packageManager = new NpmPackageManager(sourceIg, this.fhirVersion); + packageManager = new NpmPackageManager(sourceIg); // Setup binary paths (cql source directories) binaryPaths = IGUtils.extractBinaryPaths(rootDir, sourceIg); @@ -125,15 +125,15 @@ public void initializeFromIg(String rootDir, String igPath, String fhirVersion) */ public void initializeFromIni(String iniFile) { IniFile ini = new IniFile(new File(iniFile).getAbsolutePath()); - String rootDir = Utilities.getDirectoryForFile(ini.getFileName()); + String root = Utilities.getDirectoryForFile(ini.getFileName()); String igPath = ini.getStringProperty("IG", "ig"); String specifiedFhirVersion = ini.getStringProperty("IG", "fhir-version"); - if (specifiedFhirVersion == null || specifiedFhirVersion == "") { + if (specifiedFhirVersion == null || "".equals(specifiedFhirVersion)) { logMessage("fhir-version was not specified in the ini file. Trying FHIR version 4.0.1"); specifiedFhirVersion = "4.0.1"; } - initializeFromIg(rootDir, igPath, specifiedFhirVersion); + initializeFromIg(root, igPath, specifiedFhirVersion); } private List binaryPaths; @@ -166,7 +166,7 @@ public CqlProcessor getCqlProcessor() { return cqlProcessor; } - private ImplementationGuide loadSourceIG(String igPath) { + private void loadSourceIG(String igPath) { try { try { sourceIg = (ImplementationGuide) org.hl7.fhir.r5.formats.FormatUtilities.loadFile(igPath); @@ -189,10 +189,9 @@ private ImplementationGuide loadSourceIG(String igPath) { throw new IGInitializationException(String.format("error initializing IG from igPath: %s", igPath), e); } - return sourceIg; } - private ImplementationGuide loadSourceIG(String igPath, String specifiedFhirVersion) { + private void loadSourceIG(String igPath, String specifiedFhirVersion) { try { if (VersionUtilities.isR3Ver(specifiedFhirVersion)) { byte[] src = TextFile.fileToBytes(igPath); @@ -216,7 +215,6 @@ private ImplementationGuide loadSourceIG(String igPath, String specifiedFhirVers throw new IGInitializationException(message, e); } - return sourceIg; } private String determineCanonical(String url) { diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/processor/CqlProcessor.java b/tooling/src/main/java/org/opencds/cqf/tooling/processor/CqlProcessor.java index d9b85bd8e..e11500629 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/processor/CqlProcessor.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/processor/CqlProcessor.java @@ -8,6 +8,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import org.cqframework.cql.cql2elm.CqlCompilerException; import org.cqframework.cql.cql2elm.CqlTranslator; @@ -20,22 +21,24 @@ import org.cqframework.cql.cql2elm.quick.FhirLibrarySourceProvider; import org.cqframework.cql.elm.requirements.fhir.DataRequirementsProcessor; import org.cqframework.cql.elm.tracking.TrackBack; +import org.cqframework.fhir.npm.ILibraryReader; +import org.cqframework.fhir.npm.NpmLibrarySourceProvider; +import org.cqframework.fhir.npm.NpmModelInfoProvider; import org.fhir.ucum.UcumService; import org.hl7.cql.model.NamespaceInfo; import org.hl7.cql.model.NamespaceManager; +import org.hl7.elm.r1.Library; import org.hl7.elm.r1.VersionedIdentifier; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.context.IWorkerContext.ILoggingService; import org.hl7.fhir.r5.model.DataRequirement; import org.hl7.fhir.r5.model.ParameterDefinition; import org.hl7.fhir.r5.model.RelatedArtifact; +import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; -import org.opencds.cqf.tooling.npm.ILibraryReader; -import org.opencds.cqf.tooling.npm.NpmLibrarySourceProvider; -import org.opencds.cqf.tooling.npm.NpmModelInfoProvider; import org.opencds.cqf.tooling.utilities.ResourceUtils; public class CqlProcessor { @@ -43,20 +46,35 @@ public class CqlProcessor { /** * information about a cql file */ - public class CqlSourceFileInformation { + public static class CqlSourceFileInformation { + private CqlTranslatorOptions options; private VersionedIdentifier identifier; + private byte[] cql; private byte[] elm; private byte[] jsonElm; - private List errors = new ArrayList<>(); - private List relatedArtifacts = new ArrayList<>(); - private List dataRequirements = new ArrayList<>(); - private List parameters = new ArrayList<>(); + private Library library; + private final List errors = new ArrayList<>(); + private final List relatedArtifacts = new ArrayList<>(); + private final List dataRequirements = new ArrayList<>(); + private final List parameters = new ArrayList<>(); + public CqlTranslatorOptions getOptions() { + return options; + } + public void setOptions(CqlTranslatorOptions options) { + this.options = options; + } public VersionedIdentifier getIdentifier() { return identifier; } public void setIdentifier(VersionedIdentifier identifier) { this.identifier = identifier; } + public byte[] getCql() { + return cql; + } + public void setCql(byte[] cql) { + this.cql = cql; + } public byte[] getElm() { return elm; } @@ -69,6 +87,12 @@ public byte[] getJsonElm() { public void setJsonElm(byte[] jsonElm) { this.jsonElm = jsonElm; } + public Library getLibrary() { + return library; + } + public void setLibrary(Library library) { + this.library = library; + } public List getErrors() { return errors; } @@ -90,29 +114,29 @@ public List getParameters() { * library in the right order * */ - private List packages; + private final List packages; /** * All the file paths cql files might be found in (absolute local file paths) * * will be at least one error */ - private List folders; + private final List folders; /** * Version indepedent reader */ - private ILibraryReader reader; + private final ILibraryReader reader; /** * use this to write to the standard IG log */ - private ILoggingService logger; + private final ILoggingService logger; /** * UcumService used by the translator to validate UCUM units */ - private UcumService ucumService; + private final UcumService ucumService; /** * Map of translated files by fully qualified file name. @@ -126,7 +150,7 @@ public List getParameters() { * Libraries can specify a namespace, but must use this name to do it */ @SuppressWarnings("unused") - private String packageId; + private final String packageId; /** * The canonical base of the IG, used to construct a NamespaceInfo for the CQL translator @@ -134,7 +158,7 @@ public List getParameters() { * Library resources published in this IG will then have URLs of [canonicalBase]/Library/[libraryName] */ @SuppressWarnings("unused") - private String canonicalBase; + private final String canonicalBase; private NamespaceInfo namespaceInfo; @@ -177,7 +201,7 @@ public void execute() throws FHIRException { /** * Return CqlSourceFileInformation for the given filename * @param filename Fully qualified name of the source file - * @return + * @return CqlSourceFileInformation */ public CqlSourceFileInformation getFileInformation(String filename) { if (fileMap == null) { @@ -204,6 +228,10 @@ public Collection getAllFileInformation() { return this.fileMap.values(); } + public Map getFileMap() { + return this.fileMap; + } + /** * Called at the end after all getFileInformation have been called * return any errors that didn't have any particular home, and also @@ -211,7 +239,7 @@ public Collection getAllFileInformation() { * getFileInformation - these have been omitted from the IG, and that's * an error * - * @return + * @return validation messages */ public List getGeneralErrors() { List result = new ArrayList<>(); @@ -271,7 +299,7 @@ private void translateFolder(String folder) { // foreach *.cql file boolean hadCqlFiles = false; - for (File file : new File(folder).listFiles(getCqlFilenameFilter())) { + for (File file : Objects.requireNonNull(new File(folder).listFiles(getCqlFilenameFilter()))) { hadCqlFiles = true; translateFile(modelManager, libraryManager, file, options); } @@ -355,7 +383,7 @@ private void translateFile(ModelManager modelManager, LibraryManager libraryMana result.getErrors().add(exceptionToValidationMessage(file, exception)); } - if (translator.getErrors().size() > 0) { + if (!translator.getErrors().isEmpty()) { result.getErrors().add(new ValidationMessage(ValidationMessage.Source.Publisher, IssueType.EXCEPTION, file.getName(), String.format("CQL Processing failed with (%d) errors.", translator.getErrors().size()), IssueSeverity.ERROR)); logger.logMessage(String.format("Translation failed with (%d) errors; see the error log for more information.", translator.getErrors().size())); @@ -366,19 +394,22 @@ private void translateFile(ModelManager modelManager, LibraryManager libraryMana } else { try { + result.setOptions(options); // convert to base64 bytes // NOTE: Publication tooling requires XML content + result.setCql(TextFile.fileToBytes(file)); result.setElm(translator.toXml().getBytes()); - result.setIdentifier(translator.toELM().getIdentifier()); + result.setLibrary(translator.toELM()); + result.setIdentifier(result.getLibrary().getIdentifier()); if (options.getFormats().contains(CqlTranslator.Format.JSON)) { result.setJsonElm(translator.toJson().getBytes()); } // Add the translated library to the library manager (NOTE: This should be a "cacheLibrary" call on the LibraryManager, available in 1.5.3+) // Without this, the data requirements processor will try to load the current library, resulting in a re-translation - CompiledLibrary CompiledLibrary = translator.getTranslatedLibrary(); - String libraryPath = NamespaceManager.getPath(CompiledLibrary.getIdentifier().getSystem(), CompiledLibrary.getIdentifier().getId()); - libraryManager.getCompiledLibraries().put(libraryPath, CompiledLibrary); + CompiledLibrary compiledLibrary = translator.getTranslatedLibrary(); + String libraryPath = NamespaceManager.getPath(compiledLibrary.getIdentifier().getSystem(), compiledLibrary.getIdentifier().getId()); + libraryManager.getCompiledLibraries().put(libraryPath, compiledLibrary); DataRequirementsProcessor drp = new DataRequirementsProcessor(); org.hl7.fhir.r5.model.Library requirementsLibrary = @@ -415,11 +446,6 @@ private void translateFile(ModelManager modelManager, LibraryManager libraryMana } private FilenameFilter getCqlFilenameFilter() { - return new FilenameFilter() { - @Override - public boolean accept(File path, String name) { - return name.endsWith(".cql"); - } - }; + return (path, name) -> name.endsWith(".cql"); } } diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/processor/IGBundleProcessor.java b/tooling/src/main/java/org/opencds/cqf/tooling/processor/IGBundleProcessor.java index cd5825444..4b503713e 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/processor/IGBundleProcessor.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/processor/IGBundleProcessor.java @@ -21,7 +21,7 @@ public IGBundleProcessor(MeasureProcessor measureProcessor, PlanDefinitionProces this.questionnaireProcessor = questionnaireProcessor; } - public void bundleIg(ArrayList refreshedLibraryNames, String igPath, List binaryPaths, Encoding encoding, Boolean includeELM, + public void bundleIg(List refreshedLibraryNames, String igPath, List binaryPaths, Encoding encoding, Boolean includeELM, Boolean includeDependencies, Boolean includeTerminology, Boolean includePatientScenarios, Boolean versioned, Boolean addBundleTimestamp, FhirContext fhirContext, String fhirUri) { diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/processor/IGProcessor.java b/tooling/src/main/java/org/opencds/cqf/tooling/processor/IGProcessor.java index 00e22dd06..569d72dc4 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/processor/IGProcessor.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/processor/IGProcessor.java @@ -23,7 +23,8 @@ public class IGProcessor extends BaseProcessor { public static final String IG_VERSION_REQUIRED = "igVersion required"; - protected IGBundleProcessor igBundleProcessor; + public static final String INVALID_INI = "Either the ini argument or both igPath and rootDir must be provided"; + protected IGBundleProcessor igBundleProcessor; protected LibraryProcessor libraryProcessor; protected MeasureProcessor measureProcessor; @@ -47,7 +48,7 @@ public void publishIG(RefreshIGParameters params) { boolean igPathProvided = params.igPath != null && !params.igPath.isEmpty(); if (!iniProvided && (!rootDirProvided || !igPathProvided)) { - throw new IllegalArgumentException("Either the ini argument or both igPath and rootDir must be provided"); + throw new IllegalArgumentException(INVALID_INI); } if (params.ini != null) { @@ -66,7 +67,7 @@ public void publishIG(RefreshIGParameters params) { Boolean versioned = params.versioned; String fhirUri = params.fhirUri; // String measureToRefreshPath = params.measureToRefreshPath; - ArrayList resourceDirs = new ArrayList(); + ArrayList resourceDirs = new ArrayList<>(); for (String resourceDir : params.resourceDirs) { if (!Utilities.isAbsoluteFileName(resourceDir)) { try { @@ -99,13 +100,13 @@ public void publishIG(RefreshIGParameters params) { //package everything LogUtils.info("IGProcessor.publishIG - bundleIg"); igBundleProcessor.bundleIg(refreshedResourcesNames, rootDir, getBinaryPaths(), encoding, includeELM, includeDependencies, includeTerminology, includePatientScenarios, - versioned, addBundleTimestamp, fhirContext, fhirUri); + versioned, addBundleTimestamp, fhirContext, fhirUri); //test everything //IGTestProcessor.testIg(IGTestParameters); //Publish? } - public ArrayList refreshedResourcesNames = new ArrayList(); + public List refreshedResourcesNames = new ArrayList<>(); public void refreshIG(RefreshIGParameters params) { if (params.ini != null) { initializeFromIni(params.ini); @@ -129,8 +130,8 @@ public void refreshIG(RefreshIGParameters params) { Boolean versioned = params.versioned; // String fhirUri = params.fhirUri; String measureToRefreshPath = params.measureToRefreshPath; - ArrayList resourceDirs = params.resourceDirs; - if (resourceDirs.size() == 0) { + List resourceDirs = params.resourceDirs; + if (resourceDirs.isEmpty()) { try { resourceDirs = IGUtils.extractResourcePaths(this.rootDir, this.sourceIg); } catch (IOException e) { @@ -164,7 +165,7 @@ public void refreshIG(RefreshIGParameters params) { return; } - if (includePatientScenarios) { + if (Boolean.TRUE.equals(includePatientScenarios)) { TestCaseProcessor testCaseProcessor = new TestCaseProcessor(); testCaseProcessor.refreshTestCases(FilenameUtils.concat(rootDir, IGProcessor.testCasePathElement), encoding, fhirContext, refreshedResourcesNames); } @@ -188,9 +189,9 @@ public static FhirContext getIgFhirContext(String igVersion) default: throw new IllegalArgumentException("Unknown IG version: " + igVersion); - } + } } - + public static final String bundlePathElement = "bundles/"; public static String getBundlesPath(String igPath) { return FilenameUtils.concat(igPath, bundlePathElement); @@ -202,12 +203,12 @@ public static String getBundlesPath(String igPath) { public static final String valuesetsPathElement = "input/vocabulary/valueset/"; public static final String testCasePathElement = "input/tests/"; public static final String devicePathElement = "input/resources/device/"; - - public static void ensure(String igPath, Boolean includePatientScenarios, Boolean includeTerminology, ArrayList resourcePaths) { + + public static void ensure(String igPath, Boolean includePatientScenarios, Boolean includeTerminology, List resourcePaths) { File directory = new File(getBundlesPath(igPath)); if (!directory.exists()) { directory.mkdir(); - } + } if (resourcePaths.isEmpty()) { ensureDirectory(igPath, IGProcessor.cqlLibraryPathElement); ensureDirectory(igPath, IGProcessor.libraryPathElement); diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/processor/IGTestProcessor.java b/tooling/src/main/java/org/opencds/cqf/tooling/processor/IGTestProcessor.java index b1fe08bfe..8738331b1 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/processor/IGTestProcessor.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/processor/IGTestProcessor.java @@ -206,7 +206,7 @@ public void testIg(TestIGParameters params) { ITestProcessor testProcessor = getResourceTypeTestProcessor(group.getName()); List> testCasesBundles = - BundleUtils.GetBundlesInDir(testArtifact.getPath(), fhirContext, false); + BundleUtils.getBundlesInDir(testArtifact.getPath(), fhirContext, false); for (Map.Entry testCaseBundleMapEntry : testCasesBundles) { IBaseResource testCaseBundle = testCaseBundleMapEntry.getValue(); @@ -284,7 +284,7 @@ private Map.Entry getContentBundleForTestArtifact(String String contentBundlePath = getPathForContentBundleTestArtifact(groupName, testArtifactName); File testArtifactContentBundleDirectory = new File(contentBundlePath); if (testArtifactContentBundleDirectory != null && testArtifactContentBundleDirectory.exists()) { - List> testArtifactContentBundles = BundleUtils.GetBundlesInDir(contentBundlePath, fhirContext, false); + List> testArtifactContentBundles = BundleUtils.getBundlesInDir(contentBundlePath, fhirContext, false); // NOTE: Making the assumption that there will be a single bundle for the artifact. testArtifactContentBundle = testArtifactContentBundles.get(0); diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/processor/IProcessorContext.java b/tooling/src/main/java/org/opencds/cqf/tooling/processor/IProcessorContext.java index 8404adc8e..9bda614f3 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/processor/IProcessorContext.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/processor/IProcessorContext.java @@ -1,8 +1,8 @@ package org.opencds.cqf.tooling.processor; +import org.cqframework.fhir.npm.NpmPackageManager; import org.fhir.ucum.UcumService; import org.hl7.fhir.r5.model.ImplementationGuide; -import org.opencds.cqf.tooling.npm.NpmPackageManager; import java.util.List; diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/processor/PlanDefinitionProcessor.java b/tooling/src/main/java/org/opencds/cqf/tooling/processor/PlanDefinitionProcessor.java index 7e3737720..20a00f2bc 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/processor/PlanDefinitionProcessor.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/processor/PlanDefinitionProcessor.java @@ -4,17 +4,22 @@ import org.apache.commons.io.FilenameUtils; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.utilities.Utilities; import org.opencds.cqf.tooling.library.LibraryProcessor; -import org.opencds.cqf.tooling.utilities.*; +import org.opencds.cqf.tooling.utilities.BundleUtils; +import org.opencds.cqf.tooling.utilities.HttpClientUtils; +import org.opencds.cqf.tooling.utilities.IOUtils; import org.opencds.cqf.tooling.utilities.IOUtils.Encoding; +import org.opencds.cqf.tooling.utilities.LogUtils; +import org.opencds.cqf.tooling.utilities.ResourceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; -import java.util.*; -import java.util.stream.Collectors; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public class PlanDefinitionProcessor { public static final String ResourcePrefix = "plandefinition-"; @@ -28,20 +33,20 @@ public PlanDefinitionProcessor(LibraryProcessor libraryProcessor, CDSHooksProces this.cdsHooksProcessor = cdsHooksProcessor; } - public void bundlePlanDefinitions(ArrayList refreshedLibraryNames, String igPath, List binaryPaths, Boolean includeDependencies, - Boolean includeTerminology, Boolean includePatientScenarios, Boolean includeVersion, Boolean addBundleTimestamp, - FhirContext fhirContext, String fhirUri, Encoding encoding) { + public void bundlePlanDefinitions(List refreshedLibraryNames, String igPath, List binaryPaths, Boolean includeDependencies, + Boolean includeTerminology, Boolean includePatientScenarios, Boolean includeVersion, Boolean addBundleTimestamp, + FhirContext fhirContext, String fhirUri, Encoding encoding) { Map planDefinitions = IOUtils.getPlanDefinitions(fhirContext); - List bundledPlanDefinitions = new ArrayList(); + List bundledPlanDefinitions = new ArrayList<>(); for (Map.Entry planDefinitionEntry : planDefinitions.entrySet()) { String planDefinitionSourcePath = IOUtils.getPlanDefinitionPathMap(fhirContext).get(planDefinitionEntry.getKey()); // Assumption - File name matches planDefinition.name String planDefinitionName = FilenameUtils.getBaseName(planDefinitionSourcePath).replace(PlanDefinitionProcessor.ResourcePrefix, ""); try { - Map resources = new HashMap(); + Map resources = new HashMap<>(); Boolean shouldPersist = ResourceUtils.safeAddResource(planDefinitionSourcePath, resources, fhirContext); if (!resources.containsKey("PlanDefinition/" + planDefinitionEntry.getKey())) { @@ -57,18 +62,19 @@ public void bundlePlanDefinitions(ArrayList refreshedLibraryNames, Strin primaryLibrary = IOUtils.getLibraries(fhirContext).get(primaryLibraryUrl); } - if (primaryLibrary == null) + if (primaryLibrary == null) { throw new IllegalArgumentException(String.format("Could not resolve library url %s", primaryLibraryUrl)); + } String primaryLibrarySourcePath = IOUtils.getLibraryPathMap(fhirContext).get(primaryLibrary.getIdElement().getIdPart()); String primaryLibraryName = ResourceUtils.getName(primaryLibrary, fhirContext); - if (includeVersion) { + if (Boolean.TRUE.equals(includeVersion)) { primaryLibraryName = primaryLibraryName + "-" + - fhirContext.newFhirPath().evaluateFirst(primaryLibrary, "version", IBase.class).get().toString(); + fhirContext.newFhirPath().evaluateFirst(primaryLibrary, "version", IBase.class).get(); } shouldPersist = shouldPersist - & ResourceUtils.safeAddResource(primaryLibrarySourcePath, resources, fhirContext); + && ResourceUtils.safeAddResource(primaryLibrarySourcePath, resources, fhirContext); String cqlFileName = IOUtils.formatFileName(primaryLibraryName, Encoding.CQL, fhirContext); @@ -78,25 +84,25 @@ public void bundlePlanDefinitions(ArrayList refreshedLibraryNames, Strin throw new IllegalArgumentException(String.format("Could not determine CqlLibrarySource path for library %s", primaryLibraryName)); } - if (includeTerminology) { + if (Boolean.TRUE.equals(includeTerminology)) { boolean result = ValueSetsProcessor.bundleValueSets(cqlLibrarySourcePath, igPath, fhirContext, resources, encoding, includeDependencies, includeVersion); - if (shouldPersist && !result) { + if (Boolean.TRUE.equals(shouldPersist) && !result) { LogUtils.info("PlanDefinition will not be bundled because ValueSet bundling failed."); } - shouldPersist = shouldPersist & result; + shouldPersist = shouldPersist && result; } - if (includeDependencies) { + if (Boolean.TRUE.equals(includeDependencies)) { boolean result = libraryProcessor.bundleLibraryDependencies(primaryLibrarySourcePath, fhirContext, resources, encoding, includeVersion); - if (shouldPersist && !result) { + if (Boolean.TRUE.equals(shouldPersist) && !result) { LogUtils.info("PlanDefinition will not be bundled because Library Dependency bundling failed."); } - shouldPersist = shouldPersist & result; + shouldPersist = shouldPersist && result; } - if (includePatientScenarios) { + if (Boolean.TRUE.equals(includePatientScenarios)) { boolean result = TestCaseProcessor.bundleTestCases(igPath, PlanDefinitionTestGroupName, primaryLibraryName, fhirContext, resources); - if (shouldPersist && !result) { + if (Boolean.TRUE.equals(shouldPersist) && !result) { LogUtils.info("PlanDefinition will not be bundled because Test Case bundling failed."); } shouldPersist = shouldPersist & result; @@ -104,9 +110,9 @@ public void bundlePlanDefinitions(ArrayList refreshedLibraryNames, Strin List activityDefinitionPaths = CDSHooksProcessor.bundleActivityDefinitions(planDefinitionSourcePath, fhirContext, resources, encoding, includeVersion, shouldPersist); - if (shouldPersist) { + if (Boolean.TRUE.equals(shouldPersist)) { String bundleDestPath = FilenameUtils.concat(FilenameUtils.concat(IGProcessor.getBundlesPath(igPath), PlanDefinitionTestGroupName), planDefinitionName); - persistBundle(igPath, bundleDestPath, planDefinitionName, encoding, fhirContext, new ArrayList(resources.values()), fhirUri, addBundleTimestamp); + persistBundle(igPath, bundleDestPath, planDefinitionName, encoding, fhirContext, new ArrayList<>(resources.values()), fhirUri, addBundleTimestamp); bundleFiles(igPath, bundleDestPath, primaryLibraryName, binaryPaths, planDefinitionSourcePath, primaryLibrarySourcePath, fhirContext, encoding, includeTerminology, includeDependencies, includePatientScenarios, includeVersion, addBundleTimestamp); cdsHooksProcessor.addActivityDefinitionFilesToBundle(igPath, bundleDestPath, activityDefinitionPaths, fhirContext, encoding); bundledPlanDefinitions.add(planDefinitionSourcePath); @@ -171,11 +177,11 @@ private void bundleFiles(String igPath, String bundleDestPath, String libraryNam String cqlDestPath = FilenameUtils.concat(bundleDestFilesPath, cqlFileName); IOUtils.copyFile(cqlLibrarySourcePath, cqlDestPath); - if (includeTerminology) { + if (Boolean.TRUE.equals(includeTerminology)) { try { - Map valuesets = ResourceUtils.getDepValueSetResources(cqlLibrarySourcePath, igPath, fhirContext, includeDependencies, includeVersion); + Map valuesets = ResourceUtils.getDepValueSetResources(cqlLibrarySourcePath, igPath, fhirContext, includeDependencies, includeVersion); if (!valuesets.isEmpty()) { - Object bundle = BundleUtils.bundleArtifacts(ValueSetsProcessor.getId(libraryName), new ArrayList(valuesets.values()), fhirContext, addBundleTimestamp); + Object bundle = BundleUtils.bundleArtifacts(ValueSetsProcessor.getId(libraryName), new ArrayList<>(valuesets.values()), fhirContext, addBundleTimestamp); IOUtils.writeBundle(bundle, bundleDestFilesPath, encoding, fhirContext); } } catch (Exception e) { @@ -183,16 +189,16 @@ private void bundleFiles(String igPath, String bundleDestPath, String libraryNam } } - if (includeDependencies) { + if (Boolean.TRUE.equals(includeDependencies)) { Map depLibraries = ResourceUtils.getDepLibraryResources(librarySourcePath, fhirContext, encoding, includeVersion, logger); if (!depLibraries.isEmpty()) { String depLibrariesID = "library-deps-" + libraryName; - Object bundle = BundleUtils.bundleArtifacts(depLibrariesID, new ArrayList(depLibraries.values()), fhirContext, addBundleTimestamp); + Object bundle = BundleUtils.bundleArtifacts(depLibrariesID, new ArrayList<>(depLibraries.values()), fhirContext, addBundleTimestamp); IOUtils.writeBundle(bundle, bundleDestFilesPath, encoding, fhirContext); } } - if (includePatientScenarios) { + if (Boolean.TRUE.equals(includePatientScenarios)) { TestCaseProcessor.bundleTestCaseFiles(igPath, "plandefinition", libraryName, bundleDestFilesPath, fhirContext); } } diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/processor/PostBundlesInDirProcessor.java b/tooling/src/main/java/org/opencds/cqf/tooling/processor/PostBundlesInDirProcessor.java index bf30e39f7..d3881e745 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/processor/PostBundlesInDirProcessor.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/processor/PostBundlesInDirProcessor.java @@ -50,13 +50,13 @@ public static FhirContext getFhirContext(FHIRVersion fhirVersion) } } - public static void PostBundlesInDir(PostBundlesInDirParameters params) { + public static void postBundlesInDir(PostBundlesInDirParameters params) { String fhirUri = params.fhirUri; FHIRVersion fhirVersion = params.fhirVersion; Encoding encoding = params.encoding; FhirContext fhirContext = getFhirContext(fhirVersion); - List> resources = BundleUtils.GetBundlesInDir(params.directoryPath, fhirContext); + List> resources = BundleUtils.getBundlesInDir(params.directoryPath, fhirContext); resources.forEach(entry -> postBundleToFhirUri(fhirUri, encoding, fhirContext, entry.getValue())); } diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/processor/TestCaseProcessor.java b/tooling/src/main/java/org/opencds/cqf/tooling/processor/TestCaseProcessor.java index 1e8217887..708d423ff 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/processor/TestCaseProcessor.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/processor/TestCaseProcessor.java @@ -51,7 +51,7 @@ public void refreshTestCases(String path, IOUtils.Encoding encoding, FhirContext public static List getTestCaseResources(String path, FhirContext fhirContext) { - List resources = new ArrayList(); + List resources = new ArrayList<>(); List testCasePaths = IOUtils.getDirectoryPaths(path, false); for (String testCasePath : testCasePaths) { List paths = IOUtils.getFilePaths(testCasePath, true); @@ -76,7 +76,7 @@ public static String getId(String baseId) { public static Boolean bundleTestCases(String igPath, String contextResourceType, String libraryName, FhirContext fhirContext, Map resources) { - Boolean shouldPersist = true; + boolean shouldPersist = true; String igTestCasePath = FilenameUtils.concat(FilenameUtils.concat(FilenameUtils.concat(igPath, IGProcessor.testCasePathElement), contextResourceType), libraryName); // this is breaking for bundle of a bundle. Replace with individual resources diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/processor/ValueSetsProcessor.java b/tooling/src/main/java/org/opencds/cqf/tooling/processor/ValueSetsProcessor.java index 16f081abf..4dd46f1fa 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/processor/ValueSetsProcessor.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/processor/ValueSetsProcessor.java @@ -1,9 +1,9 @@ package org.opencds.cqf.tooling.processor; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import org.hl7.fhir.instance.model.api.IBaseResource; import org.opencds.cqf.tooling.utilities.IOUtils; @@ -14,25 +14,30 @@ import ca.uhn.fhir.context.FhirContext; public class ValueSetsProcessor { + + private ValueSetsProcessor() { + + } + private static Map copyToUrls(List valueSets, FhirContext fhirContext) { switch (fhirContext.getVersion().getVersion()) { - case DSTU3: - return copyToStu3Urls(valueSets, fhirContext); - case R4: - return copyToR4Urls(valueSets, fhirContext); - default: - throw new IllegalArgumentException( - "Unknown fhir version: " + fhirContext.getVersion().getVersion().getFhirVersionString()); + case DSTU3: + return copyToStu3Urls(valueSets); + case R4: + return copyToR4Urls(valueSets); + default: + throw new IllegalArgumentException( + "Unknown fhir version: " + fhirContext.getVersion().getVersion().getFhirVersionString()); } } - private static Map copyToStu3Urls(List valueSets, FhirContext fhirContext) { - Map valueSetUrls = new HashMap(); + private static Map copyToStu3Urls(List valueSets) { + Map valueSetUrls = new HashMap<>(); for (IBaseResource resource : valueSets) { if (resource instanceof org.hl7.fhir.dstu3.model.ValueSet) { valueSetUrls.putIfAbsent(((org.hl7.fhir.dstu3.model.ValueSet)resource).getUrl(), resource); } else if (resource instanceof org.hl7.fhir.dstu3.model.Bundle) { - org.hl7.fhir.dstu3.model.Bundle bundle = (org.hl7.fhir.dstu3.model.Bundle) resource; + org.hl7.fhir.dstu3.model.Bundle bundle = (org.hl7.fhir.dstu3.model.Bundle) resource; for (org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent bundleEntry : bundle.getEntry()) { org.hl7.fhir.dstu3.model.ValueSet valueSet = (org.hl7.fhir.dstu3.model.ValueSet)bundleEntry.getResource(); valueSetUrls.putIfAbsent((valueSet).getUrl(), valueSet); @@ -42,13 +47,13 @@ private static Map copyToStu3Urls(List val return valueSetUrls; } - private static Map copyToR4Urls(List valueSets, FhirContext fhirContext) { - Map valueSetUrls = new HashMap(); + private static Map copyToR4Urls(List valueSets) { + Map valueSetUrls = new HashMap<>(); for (IBaseResource resource : valueSets) { if (resource instanceof org.hl7.fhir.r4.model.ValueSet) { valueSetUrls.putIfAbsent(((org.hl7.fhir.r4.model.ValueSet)resource).getUrl(), resource); } else if (resource instanceof org.hl7.fhir.r4.model.Bundle) { - org.hl7.fhir.r4.model.Bundle bundle = (org.hl7.fhir.r4.model.Bundle) resource; + org.hl7.fhir.r4.model.Bundle bundle = (org.hl7.fhir.r4.model.Bundle) resource; for (org.hl7.fhir.r4.model.Bundle.BundleEntryComponent bundleEntry : bundle.getEntry()) { org.hl7.fhir.r4.model.ValueSet valueSet = (org.hl7.fhir.r4.model.ValueSet)bundleEntry.getResource(); valueSetUrls.putIfAbsent((valueSet).getUrl(), valueSet); @@ -61,25 +66,25 @@ private static Map copyToR4Urls(List value private static Map cachedValueSets = null; public static Map getCachedValueSets(FhirContext fhirContext) { if (cachedValueSets == null) { - IntitializeCachedValueSets(fhirContext); + intitializeCachedValueSets(fhirContext); } return cachedValueSets; } - private static void IntitializeCachedValueSets(FhirContext fhirContext) { - List allValueSetPaths = IOUtils.getTerminologyPaths(fhirContext).stream().collect(Collectors.toList()); - List allValueSets = IOUtils.readResources(allValueSetPaths, fhirContext); - + private static void intitializeCachedValueSets(FhirContext fhirContext) { + List allValueSetPaths = new ArrayList<>(IOUtils.getTerminologyPaths(fhirContext)); + List allValueSets = IOUtils.readResources(allValueSetPaths, fhirContext); + cachedValueSets = ValueSetsProcessor.copyToUrls(allValueSets, fhirContext); } - + public static String getId(String baseId) { return "valuesets-" + baseId; } public static Boolean bundleValueSets(String cqlContentPath, String igPath, FhirContext fhirContext, - Map resources, Encoding encoding, Boolean includeDependencies, Boolean includeVersion) { - Boolean shouldPersist = true; + Map resources, Encoding encoding, Boolean includeDependencies, Boolean includeVersion) { + boolean shouldPersist = true; try { Map dependencies = ResourceUtils.getDepValueSetResources(cqlContentPath, igPath, fhirContext, includeDependencies, includeVersion); for (IBaseResource resource : dependencies.values()) { diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/processor/argument/RefreshIGArgumentProcessor.java b/tooling/src/main/java/org/opencds/cqf/tooling/processor/argument/RefreshIGArgumentProcessor.java index f84efda97..80319ba07 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/processor/argument/RefreshIGArgumentProcessor.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/processor/argument/RefreshIGArgumentProcessor.java @@ -16,11 +16,9 @@ public class RefreshIGArgumentProcessor { - public static final String[] OPERATION_OPTIONS = {"RefreshIG"}; - public static final String[] INI_OPTIONS = {"ini"}; - public static final String[] ROOT_DIR_OPTIONS = {"root-dir"}; + public static final String[] ROOT_DIR_OPTIONS = {"root-dir", "rd"}; public static final String[] IG_PATH_OPTIONS = {"ip", "ig-path"}; public static final String[] IG_OUTPUT_ENCODING = {"e", "encoding"}; public static final String[] INCLUDE_ELM_OPTIONS = {"elm", "include-elm"}; @@ -89,18 +87,18 @@ public RefreshIGParameters parseAndConvert(String[] args) { String igPath = (String)options.valueOf(IG_PATH_OPTIONS[0]); List resourcePaths = ArgUtils.getOptionValues(options, RESOURCE_PATH_OPTIONS[0]); - + //could not easily use the built-in default here because it is based on the value of the igPath argument. String igEncoding = (String)options.valueOf(IG_OUTPUT_ENCODING[0]); Encoding outputEncodingEnum = Encoding.JSON; if (igEncoding != null) { outputEncodingEnum = Encoding.parse(igEncoding.toLowerCase()); } - Boolean includeELM = options.has(INCLUDE_ELM_OPTIONS[0]); - Boolean includeDependencies = options.has(INCLUDE_DEPENDENCY_LIBRARY_OPTIONS[0]); - Boolean includeTerminology = options.has(INCLUDE_TERMINOLOGY_OPTIONS[0]); - Boolean includePatientScenarios = options.has(INCLUDE_PATIENT_SCENARIOS_OPTIONS[0]); - Boolean versioned = options.has(VERSIONED_OPTIONS[0]); + boolean includeELM = options.has(INCLUDE_ELM_OPTIONS[0]); + boolean includeDependencies = options.has(INCLUDE_DEPENDENCY_LIBRARY_OPTIONS[0]); + boolean includeTerminology = options.has(INCLUDE_TERMINOLOGY_OPTIONS[0]); + boolean includePatientScenarios = options.has(INCLUDE_PATIENT_SCENARIOS_OPTIONS[0]); + boolean versioned = options.has(VERSIONED_OPTIONS[0]); String fhirUri = (String)options.valueOf(FHIR_URI_OPTIONS[0]); String measureToRefreshPath = (String)options.valueOf(MEASURE_TO_REFRESH_PATH[0]); @@ -114,21 +112,21 @@ public RefreshIGParameters parseAndConvert(String[] args) { measureOutputPath = ""; } - Boolean shouldApplySoftwareSystemStamp = true; + boolean shouldApplySoftwareSystemStamp = true; String shouldApplySoftwareSystemStampValue = (String)options.valueOf(SHOULD_APPLY_SOFTWARE_SYSTEM_STAMP_OPTIONS[0]); if ((shouldApplySoftwareSystemStampValue != null) && shouldApplySoftwareSystemStampValue.equalsIgnoreCase("false")) { shouldApplySoftwareSystemStamp = false; } - Boolean addBundleTimestamp = false; + boolean addBundleTimestamp = false; String addBundleTimestampValue = (String)options.valueOf(SHOULD_ADD_TIMESTAMP_OPTIONS[0]); if ((addBundleTimestampValue != null) && addBundleTimestampValue.equalsIgnoreCase("true")) { addBundleTimestamp = true; } - ArrayList paths = new ArrayList(); + ArrayList paths = new ArrayList<>(); if (resourcePaths != null && !resourcePaths.isEmpty()) { paths.addAll(resourcePaths); } diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/processor/argument/RefreshLibraryArgumentProcessor.java b/tooling/src/main/java/org/opencds/cqf/tooling/processor/argument/RefreshLibraryArgumentProcessor.java index f00202812..379f1e069 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/processor/argument/RefreshLibraryArgumentProcessor.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/processor/argument/RefreshLibraryArgumentProcessor.java @@ -12,18 +12,16 @@ import joptsimple.OptionSpec; import joptsimple.OptionSpecBuilder; - public class RefreshLibraryArgumentProcessor { - public static final String[] OPERATION_OPTIONS = {"RefreshLibrary"}; - - public static final String[] INI = {"ini"}; - public static final String[] IG_CANONICAL_BASE = {"igcb", "igCanonicalBase"}; - public static final String[] CQL_PATH_OPTIONS = {"cql", "content", "cqlPath", "cqlContentPath", "contentPath", "cp"}; - public static final String[] Library_PATH_OPTIONS = {"library", "libraryPath", "resourcePath", "lp", "cp"}; - public static final String[] Library_Output_DIRECTORY_OPTIONS = {"libraryOutput", "libraryOutputPath", "resourceOutputPath", "lop"}; - public static final String[] FHIR_VERSION_OPTIONS = {"fv", "fhir-version"}; - public static final String[] OUTPUT_ENCODING = {"e", "encoding"}; - public static final String[] VERSIONED_OPTIONS = {"v", "versioned"}; + static final String[] OPERATION_OPTIONS = {"RefreshLibrary"}; + static final String[] INI = {"ini"}; + static final String[] IG_CANONICAL_BASE = {"igcb", "igCanonicalBase"}; + static final String[] CQL_PATH_OPTIONS = {"cql", "content", "cqlPath", "cqlContentPath", "contentPath", "cp"}; + static final String[] Library_PATH_OPTIONS = {"library", "libraryPath", "resourcePath", "lp", "cp"}; + static final String[] Library_Output_DIRECTORY_OPTIONS = {"libraryOutput", "libraryOutputPath", "resourceOutputPath", "lop"}; + static final String[] FHIR_VERSION_OPTIONS = {"fv", "fhir-version"}; + static final String[] OUTPUT_ENCODING = {"e", "encoding"}; + static final String[] VERSIONED_OPTIONS = {"v", "versioned"}; @SuppressWarnings("unused") public OptionParser build() { @@ -76,7 +74,7 @@ public RefreshLibraryParameters parseAndConvert(String[] args) { if (encoding != null) { outputEncodingEnum = Encoding.parse(encoding.toLowerCase()); } - Boolean versioned = options.has(VERSIONED_OPTIONS[0]); + boolean versioned = options.has(VERSIONED_OPTIONS[0]); RefreshLibraryParameters lp = new RefreshLibraryParameters(); lp.ini = ini; diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/questionnaire/QuestionnaireProcessor.java b/tooling/src/main/java/org/opencds/cqf/tooling/questionnaire/QuestionnaireProcessor.java index 8d6e90050..84deacf41 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/questionnaire/QuestionnaireProcessor.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/questionnaire/QuestionnaireProcessor.java @@ -18,30 +18,29 @@ import java.util.Map; public class QuestionnaireProcessor { + private static final Logger logger = LoggerFactory.getLogger(QuestionnaireProcessor.class); public static final String ResourcePrefix = "questionnaire-"; public static final String QuestionnaireTestGroupName = "questionnaire"; private LibraryProcessor libraryProcessor; - private Logger logger = LoggerFactory.getLogger(this.getClass()); - public QuestionnaireProcessor(LibraryProcessor libraryProcessor) { this.libraryProcessor = libraryProcessor; } - public void bundleQuestionnaires(ArrayList refreshedLibraryNames, String igPath, List binaryPaths, Boolean includeDependencies, + public void bundleQuestionnaires(List refreshedLibraryNames, String igPath, List binaryPaths, Boolean includeDependencies, Boolean includeTerminology, Boolean includePatientScenarios, Boolean includeVersion, Boolean addBundleTimestamp, FhirContext fhirContext, String fhirUri, IOUtils.Encoding encoding) { Map questionnaires = IOUtils.getQuestionnaires(fhirContext); - List bundledQuestionnaires = new ArrayList(); + List bundledQuestionnaires = new ArrayList<>(); for (Map.Entry questionnaireEntry : questionnaires.entrySet()) { String questionnaireSourcePath = IOUtils.getQuestionnairePathMap(fhirContext).get(questionnaireEntry.getKey()); // Assumption - File name matches questionnaire.name String questionnaireName = FilenameUtils.getBaseName(questionnaireSourcePath).replace(org.opencds.cqf.tooling.questionnaire.QuestionnaireProcessor.ResourcePrefix, ""); try { - Map resources = new HashMap(); + Map resources = new HashMap<>(); Boolean shouldPersist = ResourceUtils.safeAddResource(questionnaireSourcePath, resources, fhirContext); if (!resources.containsKey("Questionnaire/" + questionnaireEntry.getKey())) { @@ -65,9 +64,9 @@ public void bundleQuestionnaires(ArrayList refreshedLibraryNames, String String primaryLibrarySourcePath = IOUtils.getLibraryPathMap(fhirContext).get(primaryLibrary.getIdElement().getIdPart()); String primaryLibraryName = ResourceUtils.getName(primaryLibrary, fhirContext); - if (includeVersion) { + if (Boolean.TRUE.equals(includeVersion)) { primaryLibraryName = primaryLibraryName + "-" + - fhirContext.newFhirPath().evaluateFirst(primaryLibrary, "version", IBase.class).get().toString(); + fhirContext.newFhirPath().evaluateFirst(primaryLibrary, "version", IBase.class).get(); } shouldPersist = shouldPersist @@ -81,33 +80,33 @@ public void bundleQuestionnaires(ArrayList refreshedLibraryNames, String throw new IllegalArgumentException(String.format("Could not determine CqlLibrarySource path for library %s", primaryLibraryName)); } - if (includeTerminology) { + if (Boolean.TRUE.equals(includeTerminology)) { boolean result = ValueSetsProcessor.bundleValueSets(cqlLibrarySourcePath, igPath, fhirContext, resources, encoding, includeDependencies, includeVersion); - if (shouldPersist && !result) { + if (Boolean.TRUE.equals(shouldPersist) && !result) { LogUtils.info("Questionnaire will not be bundled because ValueSet bundling failed."); } shouldPersist = shouldPersist & result; } - if (includeDependencies) { + if (Boolean.TRUE.equals(includeDependencies)) { boolean result = libraryProcessor.bundleLibraryDependencies(primaryLibrarySourcePath, fhirContext, resources, encoding, includeVersion); - if (shouldPersist && !result) { + if (Boolean.TRUE.equals(shouldPersist) && !result) { LogUtils.info("Questionnaire will not be bundled because Library Dependency bundling failed."); } shouldPersist = shouldPersist & result; } - if (includePatientScenarios) { + if (Boolean.TRUE.equals(includePatientScenarios)) { boolean result = TestCaseProcessor.bundleTestCases(igPath, QuestionnaireTestGroupName, primaryLibraryName, fhirContext, resources); - if (shouldPersist && !result) { + if (Boolean.TRUE.equals(shouldPersist) && !result) { LogUtils.info("Questionnaire will not be bundled because Test Case bundling failed."); } shouldPersist = shouldPersist & result; } - if (shouldPersist) { + if (Boolean.TRUE.equals(shouldPersist)) { String bundleDestPath = FilenameUtils.concat(FilenameUtils.concat(IGProcessor.getBundlesPath(igPath), QuestionnaireTestGroupName), questionnaireName); - persistBundle(igPath, bundleDestPath, questionnaireName, encoding, fhirContext, new ArrayList(resources.values()), fhirUri, addBundleTimestamp); + persistBundle(igPath, bundleDestPath, questionnaireName, encoding, fhirContext, new ArrayList<>(resources.values()), fhirUri, addBundleTimestamp); bundleFiles(igPath, bundleDestPath, primaryLibraryName, binaryPaths, questionnaireSourcePath, primaryLibrarySourcePath, fhirContext, encoding, includeTerminology, includeDependencies, includePatientScenarios, includeVersion, addBundleTimestamp); bundledQuestionnaires.add(questionnaireSourcePath); } @@ -171,11 +170,11 @@ private void bundleFiles(String igPath, String bundleDestPath, String libraryNam String cqlDestPath = FilenameUtils.concat(bundleDestFilesPath, cqlFileName); IOUtils.copyFile(cqlLibrarySourcePath, cqlDestPath); - if (includeTerminology) { + if (Boolean.TRUE.equals(includeTerminology)) { try { Map valuesets = ResourceUtils.getDepValueSetResources(cqlLibrarySourcePath, igPath, fhirContext, includeDependencies, includeVersion); if (!valuesets.isEmpty()) { - Object bundle = BundleUtils.bundleArtifacts(ValueSetsProcessor.getId(libraryName), new ArrayList(valuesets.values()), fhirContext, addBundleTimestamp); + Object bundle = BundleUtils.bundleArtifacts(ValueSetsProcessor.getId(libraryName), new ArrayList<>(valuesets.values()), fhirContext, addBundleTimestamp); IOUtils.writeBundle(bundle, bundleDestFilesPath, encoding, fhirContext); } } catch (Exception e) { @@ -183,16 +182,16 @@ private void bundleFiles(String igPath, String bundleDestPath, String libraryNam } } - if (includeDependencies) { + if (Boolean.TRUE.equals(includeDependencies)) { Map depLibraries = ResourceUtils.getDepLibraryResources(librarySourcePath, fhirContext, encoding, includeVersion, logger); if (!depLibraries.isEmpty()) { String depLibrariesID = "library-deps-" + libraryName; - Object bundle = BundleUtils.bundleArtifacts(depLibrariesID, new ArrayList(depLibraries.values()), fhirContext, addBundleTimestamp); + Object bundle = BundleUtils.bundleArtifacts(depLibrariesID, new ArrayList<>(depLibraries.values()), fhirContext, addBundleTimestamp); IOUtils.writeBundle(bundle, bundleDestFilesPath, encoding, fhirContext); } } - if (includePatientScenarios) { + if (Boolean.TRUE.equals(includePatientScenarios)) { TestCaseProcessor.bundleTestCaseFiles(igPath, "questionnaire", libraryName, bundleDestFilesPath, fhirContext); } } diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/utilities/ArgUtils.java b/tooling/src/main/java/org/opencds/cqf/tooling/utilities/ArgUtils.java index 1af191e12..e1d6a512e 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/utilities/ArgUtils.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/utilities/ArgUtils.java @@ -8,8 +8,13 @@ import joptsimple.OptionSet; public class ArgUtils { + public static final String[] HELP_OPTIONS = {"h", "help", "?"}; + private ArgUtils() { + + } + public static OptionSet parse(String[] args, OptionParser parser) { OptionSet options = parser.parse(args); if (options.has(HELP_OPTIONS[0])) { diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/utilities/BundleUtils.java b/tooling/src/main/java/org/opencds/cqf/tooling/utilities/BundleUtils.java index 8fa5f2519..de080ae95 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/utilities/BundleUtils.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/utilities/BundleUtils.java @@ -2,6 +2,9 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Date; @@ -9,7 +12,11 @@ import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; +import java.util.stream.Stream; +import ca.uhn.fhir.util.BundleBuilder; +import org.cqframework.fhir.utilities.exception.IGInitializationException; +import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Resource; @@ -20,6 +27,10 @@ public class BundleUtils { + private BundleUtils() { + + } + @SafeVarargs public static Object bundleArtifacts(String id, List resources, FhirContext fhirContext, Boolean addBundleTimestamp, List... identifiers) { for (IBaseResource resource : resources) { @@ -67,7 +78,7 @@ public static org.hl7.fhir.r4.model.Bundle bundleR4Artifacts(String id, List> GetBundlesInDir(String directoryPath, FhirContext fhirContext) { - return GetBundlesInDir(directoryPath, fhirContext, true); + public static List> getBundlesInDir(String directoryPath, FhirContext fhirContext) { + return getBundlesInDir(directoryPath, fhirContext, true); } - public static List> GetBundlesInDir(String directoryPath, FhirContext fhirContext, Boolean recursive) { + public static List> getBundlesInDir(String directoryPath, FhirContext fhirContext, Boolean recursive) { File dir = new File(directoryPath); if (!dir.isDirectory()) { throw new IllegalArgumentException("path to directory must be an existing directory."); @@ -142,7 +153,7 @@ public static List> GetBundlesInDir(String dire public static void stampDstu3BundleEntriesWithSoftwareSystems(org.hl7.fhir.dstu3.model.Bundle bundle, List softwareSystems, FhirContext fhirContext, String rootDir) { for (org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent entry: bundle.getEntry()) { org.hl7.fhir.dstu3.model.Resource resource = entry.getResource(); - if ((resource.fhirType().equals("Library")) || ((resource.fhirType().equals("Measure")))) { + if ((resource.fhirType().equals("Library")) || (resource.fhirType().equals("Measure"))) { org.opencds.cqf.tooling.common.stu3.CqfmSoftwareSystemHelper cqfmSoftwareSystemHelper = new org.opencds.cqf.tooling.common.stu3.CqfmSoftwareSystemHelper(rootDir); cqfmSoftwareSystemHelper.ensureSoftwareSystemExtensionAndDevice((org.hl7.fhir.dstu3.model.DomainResource)resource, softwareSystems, fhirContext); } @@ -152,7 +163,7 @@ public static void stampDstu3BundleEntriesWithSoftwareSystems(org.hl7.fhir.dstu3 public static void stampR4BundleEntriesWithSoftwareSystems(org.hl7.fhir.r4.model.Bundle bundle, List softwareSystems, FhirContext fhirContext, String rootDir) { for (org.hl7.fhir.r4.model.Bundle.BundleEntryComponent entry: bundle.getEntry()) { org.hl7.fhir.r4.model.Resource resource = entry.getResource(); - if ((resource.fhirType().equals("Library")) || ((resource.fhirType().equals("Measure")))) { + if ((resource.fhirType().equals("Library")) || (resource.fhirType().equals("Measure"))) { org.opencds.cqf.tooling.common.r4.CqfmSoftwareSystemHelper cqfmSoftwareSystemHelper = new org.opencds.cqf.tooling.common.r4.CqfmSoftwareSystemHelper(rootDir); cqfmSoftwareSystemHelper.ensureSoftwareSystemExtensionAndDevice((org.hl7.fhir.r4.model.DomainResource)resource, softwareSystems, fhirContext); } @@ -185,9 +196,8 @@ public static void extractR4Resources(org.hl7.fhir.r4.model.Bundle bundle, Strin } } - public static ArrayList getR4ResourcesFromBundle(Bundle bundle){ + public static List getR4ResourcesFromBundle(Bundle bundle){ ArrayList resourceArrayList = new ArrayList<>(); - FhirContext context = FhirContext.forR4Cached(); for (org.hl7.fhir.r4.model.Bundle.BundleEntryComponent entry : bundle.getEntry()) { org.hl7.fhir.r4.model.Resource entryResource = entry.getResource(); if (entryResource != null) { @@ -197,7 +207,7 @@ public static ArrayList getR4ResourcesFromBundle(Bundle bundle){ return resourceArrayList; } - public static ArrayList getStu3ResourcesFromBundle(org.hl7.fhir.dstu3.model.Bundle bundle){ + public static List getStu3ResourcesFromBundle(org.hl7.fhir.dstu3.model.Bundle bundle){ ArrayList resourceArrayList = new ArrayList<>(); for (org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent entry : bundle.getEntry()) { org.hl7.fhir.dstu3.model.Resource entryResource = entry.getResource(); @@ -208,4 +218,20 @@ public static ArrayList getStu3ResourcesFromB return resourceArrayList; } + public static IBaseBundle getBundleOfResourceTypeFromDirectory(String directoryPath, FhirContext fhirContext, Class clazz) { + BundleBuilder builder = new BundleBuilder(fhirContext); + try (Stream walk = Files.walk(Paths.get(directoryPath), 1)) { + walk.filter(p -> !Files.isDirectory(p)).forEach( + file -> { + IBaseResource resource = IOUtils.readResource(file.toString(), fhirContext); + if (resource != null && clazz.isAssignableFrom(resource.getClass())) { + builder.addCollectionEntry(resource); + } + } + ); + } catch (IOException ioe) { + throw new IGInitializationException("Error reading resources from path: " + directoryPath, ioe); + } + return builder.getBundle(); + } } diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/utilities/CanonicalUtils.java b/tooling/src/main/java/org/opencds/cqf/tooling/utilities/CanonicalUtils.java index c74c94dcb..7e49c6d5d 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/utilities/CanonicalUtils.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/utilities/CanonicalUtils.java @@ -84,4 +84,23 @@ public static VersionedIdentifier toVersionedIdentifier(String url) { return new VersionedIdentifier().withSystem(base).withId(id).withVersion(version); } + + public static VersionedIdentifier toVersionedIdentifierAnyResource(String url) { + String version = getVersion(url); + if ("".equals(version)) { + version = null; + } + String id = getId(url); + String head = getHead(url); + String base = null; + if (head != null) { + base = getHead(head); + } + if ("".equals(base)) { + base = null; + } + + return new VersionedIdentifier().withSystem(base).withId(id).withVersion(version); + } + } diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/utilities/IOUtils.java b/tooling/src/main/java/org/opencds/cqf/tooling/utilities/IOUtils.java index e74696214..d2e4977e8 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/utilities/IOUtils.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/utilities/IOUtils.java @@ -2,6 +2,7 @@ import java.io.BufferedReader; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; @@ -23,8 +24,11 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.cqframework.cql.cql2elm.CqlCompilerException; @@ -41,60 +45,62 @@ import ca.uhn.fhir.context.RuntimeCompositeDatatypeDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.parser.IParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class IOUtils -{ - - public enum Encoding - { - CQL("cql"), JSON("json"), XML("xml"), UNKNOWN(""); - - private String string; - - public String toString() - { - return this.string; - } - - private Encoding(String string) - { - this.string = string; +public class IOUtils { + private static final Logger logger = LoggerFactory.getLogger(IOUtils.class); + + public enum Encoding { + CQL("cql"), JSON("json"), XML("xml"), UNKNOWN(""); + + private final String value; + + @Override + public String toString() + { + return this.value; + } + + private Encoding(String string) + { + this.value = string; } public static Encoding parse(String value) { if (value == null) { return UNKNOWN; } - + switch (value.trim().toLowerCase()) { case "cql": return CQL; - case "json": + case "json": return JSON; case "xml": return XML; - default: + default: return UNKNOWN; } } - } + } - public static ArrayList resourceDirectories = new ArrayList(); + public static List resourceDirectories = new ArrayList<>(); public static String getIdFromFileName(String fileName) { - return fileName.replaceAll("_", "-"); + return fileName.replace("_", "-"); } public static byte[] encodeResource(IBaseResource resource, Encoding encoding, FhirContext fhirContext) { return encodeResource(resource, encoding, fhirContext, false); } - public static byte[] encodeResource(IBaseResource resource, Encoding encoding, FhirContext fhirContext, boolean prettyPrintOutput) - { + public static byte[] encodeResource(IBaseResource resource, Encoding encoding, FhirContext fhirContext, + boolean prettyPrintOutput) { if (encoding == Encoding.UNKNOWN) { return new byte[] { }; } - IParser parser = getParser(encoding, fhirContext); + IParser parser = getParser(encoding, fhirContext); return parser.setPrettyPrint(prettyPrintOutput).encodeResourceToString(resource).getBytes(); } @@ -102,42 +108,48 @@ public static String getFileContent(File file) { try { return FileUtils.readFileToString(file, StandardCharsets.UTF_8); } catch (IOException e) { - e.printStackTrace(); + logger.error(e.getMessage()); throw new RuntimeException("Error reading file: " + e.getMessage()); } } - public static String encodeResourceAsString(IBaseResource resource, Encoding encoding, FhirContext fhirContext) - { + public static String encodeResourceAsString(IBaseResource resource, Encoding encoding, FhirContext fhirContext) { if (encoding == Encoding.UNKNOWN) { return ""; } - IParser parser = getParser(encoding, fhirContext); - return parser.setPrettyPrint(true).encodeResourceToString(resource).toString(); + IParser parser = getParser(encoding, fhirContext); + return parser.setPrettyPrint(true).encodeResourceToString(resource); } // Issue 96 - adding second signature to allow for passing versioned - public static void writeResource(T resource, String path, Encoding encoding, FhirContext fhirContext) - { - writeResource(resource, path, encoding, fhirContext, true); + public static void writeResource(T resource, String path, Encoding encoding, + FhirContext fhirContext) { + writeResource(resource, path, encoding, fhirContext, true); } - - public static void writeResource(T resource, String path, Encoding encoding, FhirContext fhirContext, Boolean versioned) { - writeResource(resource, path, encoding, fhirContext, true, null, true); + + public static void writeResource(T resource, String path, Encoding encoding, + FhirContext fhirContext, Boolean versioned) { + writeResource(resource, path, encoding, fhirContext, versioned, null, true); } - public static void writeResource(T resource, String path, Encoding encoding, FhirContext fhirContext, Boolean versioned, String outputFileName) { - writeResource(resource, path, encoding, fhirContext, true, outputFileName, true); + public static void writeResource(T resource, String path, Encoding encoding, + FhirContext fhirContext, Boolean versioned, + String outputFileName) { + writeResource(resource, path, encoding, fhirContext, versioned, outputFileName, true); } - public static void writeResource(T resource, String path, Encoding encoding, FhirContext fhirContext, Boolean versioned, boolean prettyPrintOutput) { - writeResource(resource, path, encoding, fhirContext, true, null, prettyPrintOutput); + public static void writeResource(T resource, String path, Encoding encoding, + FhirContext fhirContext, Boolean versioned, + boolean prettyPrintOutput) { + writeResource(resource, path, encoding, fhirContext, versioned, null, prettyPrintOutput); } - public static void writeResource(T resource, String path, Encoding encoding, FhirContext fhirContext, Boolean versioned, String outputFileName, boolean prettyPrintOutput) { + public static void writeResource(T resource, String path, Encoding encoding, + FhirContext fhirContext, Boolean versioned, + String outputFileName, boolean prettyPrintOutput) { // If the path is to a specific resource file, just re-use that file path/name. - String outputPath = null; + String outputPath; File file = new File(path); if (file.isFile()) { outputPath = path; @@ -145,44 +157,38 @@ public static void writeResource(T resource, String pa else { try { ensurePath(path); - } - catch (IOException e) { - e.printStackTrace(); + } catch (IOException e) { + logger.error(e.getMessage()); throw new RuntimeException("Error writing Resource to file: " + e.getMessage()); } - String baseName = null; + String baseName; if (outputFileName == null || outputFileName.isBlank()) { baseName = resource.getIdElement().getIdPart(); } else { baseName = outputFileName; } - // Issue 96 - // If includeVersion is false then just use name and not id for the file baseName - if (!versioned) { - // Assumes that the id will be a string with - separating the version number - // baseName = baseName.split("-")[0]; - } + // Issue 96 + // If includeVersion is false then just use name and not id for the file baseName + if (Boolean.FALSE.equals(versioned)) { + // Assumes that the id will be a string with - separating the version number + // baseName = baseName.split("-")[0]; + } outputPath = FilenameUtils.concat(path, formatFileName(baseName, encoding, fhirContext)); } - try (FileOutputStream writer = new FileOutputStream(outputPath)) - { + try (FileOutputStream writer = new FileOutputStream(outputPath)) { writer.write(encodeResource(resource, encoding, fhirContext, prettyPrintOutput)); writer.flush(); - } - catch (IOException e) - { - e.printStackTrace(); + } catch (IOException e) { + logger.error(e.getMessage()); throw new RuntimeException("Error writing Resource to file: " + e.getMessage()); } } - public static void writeResources(Map resources, String path, Encoding encoding, FhirContext fhirContext) - { - for (Map.Entry set : resources.entrySet()) - { + public static void writeResources(Map resources, String path, Encoding encoding, FhirContext fhirContext) { + for (Map.Entry set : resources.entrySet()) { writeResource(set.getValue(), path, encoding, fhirContext); } } @@ -218,9 +224,8 @@ public static void copyFile(String inputPath, String outputPath) { Path src = Paths.get(inputPath); Path dest = Paths.get(outputPath); Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING); - } - catch (IOException e) { - e.printStackTrace(); + } catch (IOException e) { + logger.error(e.getMessage()); throw new RuntimeException("Error copying file: " + e.getMessage()); } } @@ -230,7 +235,6 @@ public static String getTypeQualifiedResourceId(String path, FhirContext fhirCon if (resource != null) { return resource.getIdElement().getResourceType() + "/" + resource.getIdElement().getIdPart(); } - return null; } @@ -256,23 +260,21 @@ public static String getCanonicalResourceVersion(IBaseResource resource, FhirCon public static IBaseResource readResource(String path, FhirContext fhirContext) { return readResource(path, fhirContext, false); } - + //users should always check for null - private static Map cachedResources = new LinkedHashMap(); - public static IBaseResource readResource(String path, FhirContext fhirContext, Boolean safeRead) - { + private static final Map cachedResources = new LinkedHashMap<>(); + public static IBaseResource readResource(String path, FhirContext fhirContext, Boolean safeRead) { Encoding encoding = getEncoding(path); if (encoding == Encoding.UNKNOWN || encoding == Encoding.CQL) { return null; } - IBaseResource resource = cachedResources.get(path); + IBaseResource resource = cachedResources.get(path); if (resource != null) { return resource; - } + } - try - { + try { IParser parser = getParser(encoding, fhirContext); File file = new File(path); @@ -280,40 +282,26 @@ public static IBaseResource readResource(String path, FhirContext fhirContext, B throw new IllegalArgumentException(String.format("Cannot read a resource from a directory: %s", path)); } - // if (!file.exists()) { - // String[] paths = file.getParent().split("\\\\"); - // file = new File(Paths.get(file.getParent(), paths[paths.length - 1] + "-" + file.getName()).toString()); - // } - - if (safeRead) { - if (!file.exists()) { - return null; - } + if (Boolean.TRUE.equals(safeRead) && !file.exists()) { + return null; } - try (FileReader reader = new FileReader(file)){ + try (FileReader reader = new FileReader(file)) { resource = parser.parseResource(reader); } cachedResources.put(path, resource); - } - catch (Exception e) - { + } catch (IOException e) { throw new RuntimeException(String.format("Error reading resource from path %s: %s", path, e.getMessage()), e); } return resource; } - public static void updateCachedResource(IBaseResource updatedResource, String path){ - if(null != cachedResources.get(path)){ - cachedResources.put(path, updatedResource); - } - + public static void updateCachedResource(IBaseResource updatedResource, String path) { + cachedResources.computeIfPresent(path, (key, value) -> updatedResource); } - public static List readResources(List paths, FhirContext fhirContext) - { + public static List readResources(List paths, FhirContext fhirContext) { List resources = new ArrayList<>(); - for (String path : paths) - { + for (String path : paths) { IBaseResource resource = readResource(path, fhirContext); if (resource != null) { resources.add(resource); @@ -322,36 +310,62 @@ public static List readResources(List paths, FhirContext return resources; } - public static List getFilePaths(String directoryPath, Boolean recursive) - { - List filePaths = new ArrayList(); + public static IBaseResource readJsonResourceIgnoreElements(String path, FhirContext fhirContext, String... elements) { + Encoding encoding = getEncoding(path); + if (encoding == Encoding.UNKNOWN || encoding == Encoding.CQL || encoding == Encoding.XML) { + return null; + } + + if (cachedResources.containsKey(path)) { + return cachedResources.get(path); + } + + IParser parser = getParser(encoding, fhirContext); + try (FileReader reader = new FileReader(path)) { + JsonObject obj = JsonParser.parseReader(reader).getAsJsonObject(); + Arrays.stream(elements).forEach(obj::remove); + IBaseResource resource = parser.parseResource(obj.toString()); + cachedResources.put(path, resource); + return resource; + } catch (IOException e) { + logger.error(e.getMessage()); + throw new RuntimeException(String.format("Error reading resource from path %s: %s", path, e)); + } + } + + public static List getFilePaths(String directoryPath, Boolean recursive) { + List filePaths = new ArrayList<>(); File inputDir = new File(directoryPath); - ArrayList files = inputDir.isDirectory() ? new ArrayList(Arrays.asList(Optional.ofNullable(inputDir.listFiles()).orElseThrow(() -> new NoSuchElementException()))) : new ArrayList(); - + ArrayList files = inputDir.isDirectory() + ? new ArrayList<>(Arrays.asList(Optional.ofNullable( + inputDir.listFiles()).orElseThrow(NoSuchElementException::new))) + : new ArrayList<>(); + for (File file : files) { if (file.isDirectory()) { - //note: this is not the same as anding recursive to isDirectory as that would result in directories being added to the list if the request is not recursive. - if (recursive) { + //note: this is not the same as ANDing recursive to isDirectory as that would result in directories + // being added to the list if the request is not recursive. + if (Boolean.TRUE.equals(recursive)) { filePaths.addAll(getFilePaths(file.getPath(), recursive)); } - } - else { - filePaths.add(file.getPath()); + } else { + filePaths.add(file.getPath()); } } return filePaths; } - public static String getResourceFileName(String resourcePath, IBaseResource resource, Encoding encoding, FhirContext fhirContext, boolean versioned, boolean prefixed) { + public static String getResourceFileName(String resourcePath, IBaseResource resource, Encoding encoding, + FhirContext fhirContext, boolean versioned, boolean prefixed) { String resourceVersion = IOUtils.getCanonicalResourceVersion(resource, fhirContext); String filename = resource.getIdElement().getIdPart(); // Issue 96 // Handle no version on filename but still in id if (!versioned && resourceVersion != null) { - int index = filename.indexOf(resourceVersion); - if (index > 0) { - filename = filename.substring(0, index - 1); - } + int index = filename.indexOf(resourceVersion); + if (index > 0) { + filename = filename.substring(0, index - 1); + } } else if (versioned && resourceVersion != null) { int index = filename.indexOf(resourceVersion); if (index < 0) { @@ -360,11 +374,8 @@ public static String getResourceFileName(String resourcePath, IBaseResource reso } String resourceType = resource.fhirType().toLowerCase(); - // Cannot read from here it isn't always set - //String resourceType = resource.getIdElement().getResourceType().toLowerCase(); - - String result = Paths.get(resourcePath, resourceType, (prefixed ? (resourceType + "-") : "") + filename) + getFileExtension(encoding); - return result; + return Paths.get(resourcePath, resourceType, (prefixed ? (resourceType + "-") : "") + + filename) + getFileExtension(encoding); } // Returns the parent directory if it is named resources, otherwise, the parent of that @@ -373,7 +384,6 @@ public static String getResourceDirectory(String path) { if (!result.toLowerCase().endsWith("resources")) { result = getParentDirectoryPath(result); } - return result; } @@ -382,22 +392,22 @@ public static String getParentDirectoryPath(String path) { return file.getParent(); } - public static List getDirectoryPaths(String path, Boolean recursive) - { - List directoryPaths = new ArrayList(); - List directories = new ArrayList(); + public static List getDirectoryPaths(String path, Boolean recursive) { + List directoryPaths = new ArrayList<>(); + List directories; File parentDirectory = new File(path); try { - directories = Arrays.asList(Optional.ofNullable(parentDirectory.listFiles()).orElseThrow(() -> new NoSuchElementException())); + directories = Arrays.asList(Optional.ofNullable(parentDirectory.listFiles()) + .orElseThrow(NoSuchElementException::new)); } catch (Exception e) { - System.out.println("No paths found for the Directory " + path + ":"); + logger.error("No paths found for the Directory {}:", path); return directoryPaths; } - - + + for (File directory : directories) { if (directory.isDirectory()) { - if (recursive) { + if (Boolean.TRUE.equals(recursive)) { directoryPaths.addAll(getDirectoryPaths(directory.getPath(), recursive)); } directoryPaths.add(directory.getPath()); @@ -412,21 +422,24 @@ public static void initializeDirectory(String path) { try { deleteDirectory(path); } catch (IOException e) { - e.printStackTrace(); + logger.error(e.getMessage()); throw new RuntimeException("Error deleting directory: " + path + " - " + e.getMessage()); } } - directory.mkdir(); + + if (!directory.mkdir()) { + logger.warn("Unable to initialize directory at {}", path); + } } public static void deleteDirectory(String path) throws IOException { - Files.walkFileTree(Paths.get(path), new SimpleFileVisitor() { + Files.walkFileTree(Paths.get(path), new SimpleFileVisitor<>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) throws IOException { Files.delete(file); // this will work because it's always a File return FileVisitResult.CONTINUE; } - + @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { Files.delete(dir); //this will work because Files in the directory are already deleted @@ -441,99 +454,90 @@ public static Encoding getEncoding(String path) } //users should protect against Encoding.UNKNOWN or Enconding.CQL - private static IParser getParser(Encoding encoding, FhirContext fhirContext) - { + private static IParser getParser(Encoding encoding, FhirContext fhirContext) { switch (encoding) { - case XML: + case XML: return fhirContext.newXmlParser(); case JSON: return fhirContext.newJsonParser(); - default: - throw new RuntimeException("Unknown encoding type: " + encoding.toString()); + default: + throw new RuntimeException("Unknown encoding type: " + encoding); } } - public static Boolean pathEndsWithElement(String igPath, String pathElement) - { - Boolean result = false; - try - { + public static Boolean pathEndsWithElement(String igPath, String pathElement) { + boolean result = false; + try { String baseElement = FilenameUtils.getBaseName(igPath).equals("") ? FilenameUtils.getBaseName(FilenameUtils.getFullPathNoEndSeparator(igPath)) : FilenameUtils.getBaseName(igPath); result = baseElement.equals(pathElement); - } - catch (Exception e) {} + } catch (Exception ignored) {} return result; } - public static List getDependencyCqlPaths(String cqlContentPath, Boolean includeVersion) throws Exception { - ArrayList DependencyFiles = getDependencyCqlFiles(cqlContentPath, includeVersion); - ArrayList DependencyPaths = new ArrayList(); - for (File file : DependencyFiles) { - DependencyPaths.add(file.getPath().toString()); + public static List getDependencyCqlPaths(String cqlContentPath, Boolean includeVersion) { + List dependencyFiles = getDependencyCqlFiles(cqlContentPath, includeVersion); + List dependencyPaths = new ArrayList<>(); + for (File file : dependencyFiles) { + dependencyPaths.add(file.getPath()); } - return DependencyPaths; + return dependencyPaths; } - public static ArrayList getDependencyCqlFiles(String cqlContentPath, Boolean includeVersion) throws Exception { + public static List getDependencyCqlFiles(String cqlContentPath, Boolean includeVersion) { File cqlContent = new File(cqlContentPath); File cqlContentDir = cqlContent.getParentFile(); if (!cqlContentDir.isDirectory()) { throw new IllegalArgumentException("The specified path to library files is not a directory"); } - ArrayList dependencyLibraries = ResourceUtils.getIncludedLibraryNames(cqlContentPath, includeVersion); + + List dependencyLibraries = ResourceUtils.getIncludedLibraryNames(cqlContentPath, includeVersion); File[] allCqlContentFiles = cqlContentDir.listFiles(); - if (allCqlContentFiles.length == 1) { - return new ArrayList(); - } ArrayList dependencyCqlFiles = new ArrayList<>(); - for (File cqlFile : allCqlContentFiles) { - if (dependencyLibraries.contains(getIdFromFileName(cqlFile.getName().replace(".cql", "")))) { - dependencyCqlFiles.add(cqlFile); - dependencyLibraries.remove(getIdFromFileName(cqlFile.getName().replace(".cql", ""))); - } + + if (allCqlContentFiles != null) { + if (allCqlContentFiles.length == 1) { + return new ArrayList<>(); + } + for (File cqlFile : allCqlContentFiles) { + if (dependencyLibraries.contains(getIdFromFileName(cqlFile.getName().replace(".cql", "")))) { + dependencyCqlFiles.add(cqlFile); + dependencyLibraries.remove(getIdFromFileName(cqlFile.getName().replace(".cql", ""))); + } + } } - if (dependencyLibraries.size() != 0) { - String message = (dependencyLibraries.size()) + " included cql Libraries not found: "; - + if (!dependencyLibraries.isEmpty()) { + StringBuilder message = new StringBuilder().append(dependencyLibraries.size()) + .append(" included cql Libraries not found: "); + for (String includedLibrary : dependencyLibraries) { - message += "\r\n" + includedLibrary + " MISSING"; - } - throw new Exception(message); - } + message.append("\r\n").append(includedLibrary).append(" MISSING"); + } + throw new RuntimeException(message.toString()); + } return dependencyCqlFiles; - } - - private static Map cachedTranslator = new LinkedHashMap(); + } + + private static final Map cachedTranslator = new LinkedHashMap<>(); public static CqlTranslator translate(String cqlContentPath, ModelManager modelManager, LibraryManager libraryManager, CqlTranslatorOptions options) { CqlTranslator translator = cachedTranslator.get(cqlContentPath); if (translator != null) { return translator; } try { - File cqlFile = new File(cqlContentPath); - if(!cqlFile.getName().endsWith(".cql")) { - throw new IllegalArgumentException("cqlContentPath must be a path to a .cql file"); - } - - // ArrayList options = new ArrayList<>(); - // options.add(CqlTranslatorOptions.Options.EnableDateRangeOptimization); - - translator = - CqlTranslator.fromFile( - cqlFile, - modelManager, - libraryManager, - null, options); - - if (translator.getErrors().size() > 0) { - //System.err.println("Translation failed due to errors:"); + File cqlFile = new File(cqlContentPath); + if (!cqlFile.getName().endsWith(".cql")) { + throw new IllegalArgumentException("cqlContentPath must be a path to a .cql file"); + } + + translator = CqlTranslator.fromFile(cqlFile, modelManager, libraryManager, null, options); + + if (!translator.getErrors().isEmpty()) { ArrayList errors = new ArrayList<>(); for (CqlCompilerException error : translator.getErrors()) { TrackBack tb = error.getLocator(); String lines = tb == null ? "[n/a]" : String.format("[%d:%d, %d:%d]", tb.getStartLine(), tb.getStartChar(), tb.getEndLine(), tb.getEndChar()); - //System.err.printf("%s %s%n", lines, error.getMessage()); errors.add(lines + error.getMessage()); } throw new IllegalArgumentException(errors.toString()); @@ -541,9 +545,7 @@ public static CqlTranslator translate(String cqlContentPath, ModelManager modelM cachedTranslator.put(cqlContentPath, translator); return translator; } catch (IOException e) { - //e.printStackTrace(); - //throw new IllegalArgumentException("Error encountered during CQL translation: " + e.getMessage()); - throw new IllegalArgumentException("Error encountered during CQL translation"); + throw new IllegalArgumentException("Error encountered during CQL translation", e); } } @@ -556,7 +558,7 @@ public static String getCqlString(String cqlContentPath) { cql.append(line).append("\n"); } } catch (IOException e) { - e.printStackTrace(); + logger.error(e.getMessage()); throw new IllegalArgumentException("Error reading CQL file: " + cqlFile.getName()); } return cql.toString(); @@ -579,30 +581,26 @@ public static String formatFileName(String baseName, Encoding encoding, FhirCont default: igVersionToken = ""; } - String result = baseName + getFileExtension(encoding); + String result = baseName + getFileExtension(encoding); if (encoding == Encoding.CQL) { result = result.replace("-" + igVersionToken, "_" + igVersionToken); } return result; - } + } - public static List putAllInListIfAbsent(List values, List list) - { + public static void putAllInListIfAbsent(List values, List list) { for (String value : values) { if (!list.contains(value)) { list.add(value); } } - return list; } - public static List putInListIfAbsent(String value, List list) - { + public static void putInListIfAbsent(String value, List list) { if (!list.contains(value)) { list.add(value); } - return list; } public static String getLibraryPathAssociatedWithCqlFileName(String cqlPath, FhirContext fhirContext) { @@ -613,10 +611,10 @@ public static String getLibraryPathAssociatedWithCqlFileName(String cqlPath, Fhi // NOTE: A bit of a hack, but we need to support both xml and json encodings for existing resources and the long-term strategy is // to revisit this and change the approach to use the references rather than file name matching, so this should be good for the near-term. if (path.endsWith(libraryFileName.replaceAll(".cql", ".json")) - || path.endsWith(libraryFileName.replaceAll(".cql", ".xml")) - || path.endsWith(fileName.replaceAll(".cql", ".json")) - || path.endsWith(fileName.replaceAll(".cql", ".xml"))) - { + || path.endsWith(libraryFileName.replaceAll(".cql", ".xml")) + || path.endsWith(fileName.replaceAll(".cql", ".json")) + || path.endsWith(fileName.replaceAll(".cql", ".xml"))) + { libraryPath = path; break; } @@ -625,18 +623,18 @@ public static String getLibraryPathAssociatedWithCqlFileName(String cqlPath, Fhi return libraryPath; } - private static HashSet cqlLibraryPaths = new LinkedHashSet(); - public static HashSet getCqlLibraryPaths() { + private static final HashSet cqlLibraryPaths = new LinkedHashSet<>(); + public static Set getCqlLibraryPaths() { if (cqlLibraryPaths.isEmpty()) { setupCqlLibraryPaths(); } return cqlLibraryPaths; } - private static void setupCqlLibraryPaths() { - //need to add a error report for bad resource paths - for(String dir : resourceDirectories) { + private static void setupCqlLibraryPaths() { + //need to add an error report for bad resource paths + for (String dir : resourceDirectories) { List filePaths = IOUtils.getFilePaths(dir, true); - filePaths.stream().filter(path -> path.contains(".cql")).forEach(path -> cqlLibraryPaths.add(path)); + filePaths.stream().filter(path -> path.contains(".cql")).forEach(cqlLibraryPaths::add); } } @@ -658,32 +656,30 @@ public static String getCqlLibrarySourcePath(String libraryName, String cqlFileN } } } - } - catch (IOException e) { - e.printStackTrace(); + } catch (IOException e) { + logger.error(e.getMessage()); LogUtils.putException(libraryName, e); } return cqlLibrarySourcePath; } - private static HashSet terminologyPaths = new LinkedHashSet(); - public static HashSet getTerminologyPaths(FhirContext fhirContext) { + private static final HashSet terminologyPaths = new LinkedHashSet<>(); + public static Set getTerminologyPaths(FhirContext fhirContext) { if (terminologyPaths.isEmpty()) { setupTerminologyPaths(fhirContext); } return terminologyPaths; } private static void setupTerminologyPaths(FhirContext fhirContext) { - HashMap resources = new LinkedHashMap(); - for(String dir : resourceDirectories) { - for(String path : IOUtils.getFilePaths(dir, true)) - { + HashMap resources = new LinkedHashMap<>(); + for (String dir : resourceDirectories) { + for (String path : IOUtils.getFilePaths(dir, true)) { try { resources.put(path, IOUtils.readResource(path, fhirContext, true)); } catch (Exception e) { if (path.toLowerCase().contains("valuesets") || path.toLowerCase().contains("valueset")) { - System.out.println("Error reading in Terminology from path: " + path + "\n" + e); + logger.error("Error reading in Terminology from path: {} \n {}", path, e); } } } @@ -695,13 +691,13 @@ private static void setupTerminologyPaths(FhirContext fhirContext) { String conceptClassName = conceptDefinition.getImplementingClass().getName(); String codingClassName = codingDefinition.getImplementingClass().getName(); resources.entrySet().stream() - .filter(entry -> entry.getValue() != null) - .filter(entry -> - valuesetClassName.equals(entry.getValue().getClass().getName()) - || conceptClassName.equals(entry.getValue().getClass().getName()) - || codingClassName.equals(entry.getValue().getClass().getName()) - ) - .forEach(entry -> terminologyPaths.add(entry.getKey())); + .filter(entry -> entry.getValue() != null) + .filter(entry -> + valuesetClassName.equals(entry.getValue().getClass().getName()) + || conceptClassName.equals(entry.getValue().getClass().getName()) + || codingClassName.equals(entry.getValue().getClass().getName()) + ) + .forEach(entry -> terminologyPaths.add(entry.getKey())); } } @@ -713,14 +709,14 @@ public static IBaseResource getLibraryByUrl(FhirContext fhirContext, String url) return library; } - private static HashSet libraryPaths = new LinkedHashSet(); - public static HashSet getLibraryPaths(FhirContext fhirContext) { + private static final HashSet libraryPaths = new LinkedHashSet<>(); + public static Set getLibraryPaths(FhirContext fhirContext) { if (libraryPaths.isEmpty()) { setupLibraryPaths(fhirContext); } return libraryPaths; } - private static Map libraryUrlMap = new LinkedHashMap(); + private static final Map libraryUrlMap = new LinkedHashMap<>(); public static Map getLibraryUrlMap(FhirContext fhirContext) { if (libraryPathMap.isEmpty()) { setupLibraryPaths(fhirContext); @@ -731,14 +727,14 @@ public static Map getLibraryUrlMap(FhirContext fhirContex } return libraryUrlMap; } - private static Map libraryPathMap = new LinkedHashMap(); + private static final Map libraryPathMap = new LinkedHashMap<>(); public static Map getLibraryPathMap(FhirContext fhirContext) { if (libraryPathMap.isEmpty()) { setupLibraryPaths(fhirContext); } return libraryPathMap; } - private static Map libraries = new LinkedHashMap(); + private static final Map libraries = new LinkedHashMap<>(); public static Map getLibraries(FhirContext fhirContext) { if (libraries.isEmpty()) { setupLibraryPaths(fhirContext); @@ -746,16 +742,15 @@ public static Map getLibraries(FhirContext fhirContext) { return libraries; } private static void setupLibraryPaths(FhirContext fhirContext) { - Map resources = new LinkedHashMap(); - for(String dir : resourceDirectories) { - for(String path : IOUtils.getFilePaths(dir, true)) - { + Map resources = new LinkedHashMap<>(); + for (String dir : resourceDirectories) { + for(String path : IOUtils.getFilePaths(dir, true)) { try { IBaseResource resource = IOUtils.readResource(path, fhirContext, true); resources.put(path, resource); } catch (Exception e) { if(path.toLowerCase().contains("library")) { - System.out.println("Error reading in Library from path: " + path + "\n" + e); + logger.error("Error reading in Library from path: {} \n {}", path, e); } } } @@ -764,32 +759,32 @@ private static void setupLibraryPaths(FhirContext fhirContext) { String libraryClassName = libraryDefinition.getImplementingClass().getName(); // BaseRuntimeChildDefinition urlElement = libraryDefinition.getChildByNameOrThrowDataFormatException("url"); resources.entrySet().stream() - .filter(entry -> entry.getValue() != null) - .filter(entry -> libraryClassName.equals(entry.getValue().getClass().getName())) - .forEach(entry -> { - libraryPaths.add(entry.getKey()); - libraries.put(entry.getValue().getIdElement().getIdPart(), entry.getValue()); - libraryPathMap.put(entry.getValue().getIdElement().getIdPart(), entry.getKey()); - libraryUrlMap.put(ResourceUtils.getUrl(entry.getValue(), fhirContext), entry.getValue()); - }); + .filter(entry -> entry.getValue() != null) + .filter(entry -> libraryClassName.equals(entry.getValue().getClass().getName())) + .forEach(entry -> { + libraryPaths.add(entry.getKey()); + libraries.put(entry.getValue().getIdElement().getIdPart(), entry.getValue()); + libraryPathMap.put(entry.getValue().getIdElement().getIdPart(), entry.getKey()); + libraryUrlMap.put(ResourceUtils.getUrl(entry.getValue(), fhirContext), entry.getValue()); + }); } } - private static HashSet measurePaths = new LinkedHashSet(); - public static HashSet getMeasurePaths(FhirContext fhirContext) { + private static final HashSet measurePaths = new LinkedHashSet<>(); + public static Set getMeasurePaths(FhirContext fhirContext) { if (measurePaths.isEmpty()) { setupMeasurePaths(fhirContext); } return measurePaths; } - private static Map measurePathMap = new LinkedHashMap(); + private static final Map measurePathMap = new LinkedHashMap<>(); public static Map getMeasurePathMap(FhirContext fhirContext) { if (measurePathMap.isEmpty()) { setupMeasurePaths(fhirContext); } return measurePathMap; } - private static Map measures = new LinkedHashMap(); + private static final Map measures = new LinkedHashMap<>(); public static Map getMeasures(FhirContext fhirContext) { if (measures.isEmpty()) { setupMeasurePaths(fhirContext); @@ -797,16 +792,15 @@ public static Map getMeasures(FhirContext fhirContext) { return measures; } private static void setupMeasurePaths(FhirContext fhirContext) { - Map resources = new LinkedHashMap(); - for(String dir : resourceDirectories) { - for(String path : IOUtils.getFilePaths(dir, true)) - { + Map resources = new LinkedHashMap<>(); + for (String dir : resourceDirectories) { + for(String path : IOUtils.getFilePaths(dir, true)) { try { IBaseResource resource = IOUtils.readResource(path, fhirContext, true); resources.put(path, resource); } catch (Exception e) { if(path.toLowerCase().contains("measure")) { - System.out.println("Error reading in Measure from path: " + path + "\n" + e); + logger.error("Error reading in Measure from path: {} \n {}", path, e); } } } @@ -814,28 +808,27 @@ private static void setupMeasurePaths(FhirContext fhirContext) { RuntimeResourceDefinition measureDefinition = ResourceUtils.getResourceDefinition(fhirContext, "Measure"); String measureClassName = measureDefinition.getImplementingClass().getName(); resources.entrySet().stream() - .filter(entry -> entry.getValue() != null) - .filter(entry -> measureClassName.equals(entry.getValue().getClass().getName())) - .forEach(entry -> { - measurePaths.add(entry.getKey()); - measures.put(entry.getValue().getIdElement().getIdPart(), entry.getValue()); - measurePathMap.put(entry.getValue().getIdElement().getIdPart(), entry.getKey()); - }); + .filter(entry -> entry.getValue() != null) + .filter(entry -> measureClassName.equals(entry.getValue().getClass().getName())) + .forEach(entry -> { + measurePaths.add(entry.getKey()); + measures.put(entry.getValue().getIdElement().getIdPart(), entry.getValue()); + measurePathMap.put(entry.getValue().getIdElement().getIdPart(), entry.getKey()); + }); } } - private static HashSet measureReportPaths = new LinkedHashSet(); - public static HashSet getMeasureReportPaths(FhirContext fhirContext) { + private static final HashSet measureReportPaths = new LinkedHashSet<>(); + public static Set getMeasureReportPaths(FhirContext fhirContext) { if (measureReportPaths.isEmpty()) { setupMeasureReportPaths(fhirContext); } return measureReportPaths; } private static void setupMeasureReportPaths(FhirContext fhirContext) { - HashMap resources = new LinkedHashMap(); - for(String dir : resourceDirectories) { - for(String path : IOUtils.getFilePaths(dir, true)) - { + HashMap resources = new LinkedHashMap<>(); + for (String dir : resourceDirectories) { + for(String path : IOUtils.getFilePaths(dir, true)) { try { resources.put(path, IOUtils.readResource(path, fhirContext, true)); } catch (Exception e) { @@ -846,27 +839,27 @@ private static void setupMeasureReportPaths(FhirContext fhirContext) { RuntimeResourceDefinition measureReportDefinition = ResourceUtils.getResourceDefinition(fhirContext, "MeasureReport"); String measureReportClassName = measureReportDefinition.getImplementingClass().getName(); resources.entrySet().stream() - .filter(entry -> entry.getValue() != null) - .filter(entry -> measureReportClassName.equals(entry.getValue().getClass().getName())) - .forEach(entry -> measureReportPaths.add(entry.getKey())); + .filter(entry -> entry.getValue() != null) + .filter(entry -> measureReportClassName.equals(entry.getValue().getClass().getName())) + .forEach(entry -> measureReportPaths.add(entry.getKey())); } } - private static HashSet planDefinitionPaths = new LinkedHashSet(); - public static HashSet getPlanDefinitionPaths(FhirContext fhirContext) { + private static final HashSet planDefinitionPaths = new LinkedHashSet<>(); + public static Set getPlanDefinitionPaths(FhirContext fhirContext) { if (planDefinitionPaths.isEmpty()) { setupPlanDefinitionPaths(fhirContext); } return planDefinitionPaths; } - private static Map planDefinitionPathMap = new LinkedHashMap(); + private static final Map planDefinitionPathMap = new LinkedHashMap<>(); public static Map getPlanDefinitionPathMap(FhirContext fhirContext) { if (planDefinitionPathMap.isEmpty()) { setupPlanDefinitionPaths(fhirContext); } return planDefinitionPathMap; } - private static Map planDefinitions = new LinkedHashMap(); + private static final Map planDefinitions = new LinkedHashMap<>(); public static Map getPlanDefinitions(FhirContext fhirContext) { if (planDefinitions.isEmpty()) { setupPlanDefinitionPaths(fhirContext); @@ -874,38 +867,37 @@ public static Map getPlanDefinitions(FhirContext fhirCont return planDefinitions; } private static void setupPlanDefinitionPaths(FhirContext fhirContext) { - HashMap resources = new LinkedHashMap(); - for(String dir : resourceDirectories) { - for(String path : IOUtils.getFilePaths(dir, true)) - { + HashMap resources = new LinkedHashMap<>(); + for (String dir : resourceDirectories) { + for(String path : IOUtils.getFilePaths(dir, true)) { try { resources.put(path, IOUtils.readResource(path, fhirContext, true)); } catch (Exception e) { - System.out.println(String.format("Error setting PlanDefinition paths while reading resource at: '%s'. Error: %s", path, e.getMessage())); + logger.error("Error setting PlanDefinition paths while reading resource at: {}. Error: {}", path, e.getMessage()); } } RuntimeResourceDefinition planDefinitionDefinition = ResourceUtils.getResourceDefinition(fhirContext, "PlanDefinition"); String planDefinitionClassName = planDefinitionDefinition.getImplementingClass().getName(); resources.entrySet().stream() - .filter(entry -> entry.getValue() != null) - .filter(entry -> planDefinitionClassName.equals(entry.getValue().getClass().getName())) - .forEach(entry -> { - planDefinitionPaths.add(entry.getKey()); - planDefinitions.put(entry.getValue().getIdElement().getIdPart(), entry.getValue()); - planDefinitionPathMap.put(entry.getValue().getIdElement().getIdPart(), entry.getKey()); - }); + .filter(entry -> entry.getValue() != null) + .filter(entry -> planDefinitionClassName.equals(entry.getValue().getClass().getName())) + .forEach(entry -> { + planDefinitionPaths.add(entry.getKey()); + planDefinitions.put(entry.getValue().getIdElement().getIdPart(), entry.getValue()); + planDefinitionPathMap.put(entry.getValue().getIdElement().getIdPart(), entry.getKey()); + }); } } - private static HashSet questionnairePaths = new LinkedHashSet(); - public static HashSet getQuestionnairePaths(FhirContext fhirContext) { + private static final HashSet questionnairePaths = new LinkedHashSet<>(); + public static Set getQuestionnairePaths(FhirContext fhirContext) { if (questionnairePaths.isEmpty()) { setupQuestionnairePaths(fhirContext); } return questionnairePaths; } - private static Map questionnairePathMap = new LinkedHashMap(); + private static final Map questionnairePathMap = new LinkedHashMap<>(); public static Map getQuestionnairePathMap(FhirContext fhirContext) { if (questionnairePathMap.isEmpty()) { setupQuestionnairePaths(fhirContext); @@ -913,7 +905,7 @@ public static Map getQuestionnairePathMap(FhirContext fhirContex return questionnairePathMap; } - private static Map questionnaires = new LinkedHashMap(); + private static final Map questionnaires = new LinkedHashMap<>(); public static Map getQuestionnaires(FhirContext fhirContext) { if (questionnaires.isEmpty()) { setupQuestionnairePaths(fhirContext); @@ -922,14 +914,13 @@ public static Map getQuestionnaires(FhirContext fhirConte } private static void setupQuestionnairePaths(FhirContext fhirContext) { - HashMap resources = new LinkedHashMap(); - for(String dir : resourceDirectories) { - for(String path : IOUtils.getFilePaths(dir, true)) - { + HashMap resources = new LinkedHashMap<>(); + for (String dir : resourceDirectories) { + for(String path : IOUtils.getFilePaths(dir, true)) { try { resources.put(path, IOUtils.readResource(path, fhirContext, true)); } catch (Exception e) { - System.out.println(String.format("Error setting Questionnaire paths while reading resource at: '%s'. Error: %s", path, e.getMessage())); + logger.error("Error setting Questionnaire paths while reading resource at: {}. Error: {}", path, e.getMessage()); } } RuntimeResourceDefinition questionnaireDefinition = ResourceUtils.getResourceDefinition(fhirContext, "Questionnaire"); @@ -945,23 +936,22 @@ private static void setupQuestionnairePaths(FhirContext fhirContext) { } } - private static HashSet activityDefinitionPaths = new LinkedHashSet(); - public static HashSet getActivityDefinitionPaths(FhirContext fhirContext) { + private static final HashSet activityDefinitionPaths = new LinkedHashSet<>(); + public static Set getActivityDefinitionPaths(FhirContext fhirContext) { if (activityDefinitionPaths.isEmpty()) { - System.out.println("Reading activitydefinitions"); + logger.info("Reading activitydefinitions"); setupActivityDefinitionPaths(fhirContext); } return activityDefinitionPaths; } private static void setupActivityDefinitionPaths(FhirContext fhirContext) { - HashMap resources = new LinkedHashMap(); + HashMap resources = new LinkedHashMap<>(); // BUG: resourceDirectories is being populated with all "per-convention" directories during validation. So, // if you have resources in the /tests directory for example, they will be picked up from there, rather than // from your resources directories. - for(String dir : resourceDirectories) { - for(String path : IOUtils.getFilePaths(dir, true)) - { + for (String dir : resourceDirectories) { + for(String path : IOUtils.getFilePaths(dir, true)) { try { resources.put(path, IOUtils.readResource(path, fhirContext, true)); } catch (Exception e) { @@ -971,9 +961,9 @@ private static void setupActivityDefinitionPaths(FhirContext fhirContext) { RuntimeResourceDefinition activityDefinitionDefinition = ResourceUtils.getResourceDefinition(fhirContext, "ActivityDefinition"); String activityDefinitionClassName = activityDefinitionDefinition.getImplementingClass().getName(); resources.entrySet().stream() - .filter(entry -> entry.getValue() != null) - .filter(entry -> activityDefinitionClassName.equals(entry.getValue().getClass().getName())) - .forEach(entry -> activityDefinitionPaths.add(entry.getKey())); + .filter(entry -> entry.getValue() != null) + .filter(entry -> activityDefinitionClassName.equals(entry.getValue().getClass().getName())) + .forEach(entry -> activityDefinitionPaths.add(entry.getKey())); } } @@ -981,15 +971,13 @@ public static void ensurePath(String path) throws IOException { //Creating a File object File scopeDir = new File(path); //Creating the directory - if (!scopeDir.exists()) { - if (!scopeDir.mkdirs()) { - throw new IOException("Could not create directory: " + path); - } + if (!scopeDir.exists() && !scopeDir.mkdirs()) { + throw new IOException("Could not create directory: " + path); } } private static HashSet devicePaths; - public static HashSet getDevicePaths(FhirContext fhirContext) { + public static Set getDevicePaths(FhirContext fhirContext) { if (devicePaths == null) { setupDevicePaths(fhirContext); } @@ -1002,16 +990,15 @@ public static void clearDevicePaths() { } private static void setupDevicePaths(FhirContext fhirContext) { - devicePaths = new LinkedHashSet(); - HashMap resources = new LinkedHashMap(); - for(String dir : resourceDirectories) { - for(String path : IOUtils.getFilePaths(dir, true)) - { + devicePaths = new LinkedHashSet<>(); + HashMap resources = new LinkedHashMap<>(); + for (String dir : resourceDirectories) { + for(String path : IOUtils.getFilePaths(dir, true)) { try { resources.put(path, IOUtils.readResource(path, fhirContext, true)); } catch (Exception e) { if(path.toLowerCase().contains("device")) { - System.out.println("Error reading in Device from path: " + path + "\n" + e); + logger.error("Error reading in Device from path: {} \n {}", path, e); } } } @@ -1019,19 +1006,19 @@ private static void setupDevicePaths(FhirContext fhirContext) { RuntimeResourceDefinition deviceDefinition = ResourceUtils.getResourceDefinition(fhirContext, "Device"); String deviceClassName = deviceDefinition.getImplementingClass().getName(); resources.entrySet().stream() - .filter(entry -> entry.getValue() != null) - .filter(entry -> deviceClassName.equals(entry.getValue().getClass().getName())) - .forEach(entry -> devicePaths.add(entry.getKey())); + .filter(entry -> entry.getValue() != null) + .filter(entry -> deviceClassName.equals(entry.getValue().getClass().getName())) + .forEach(entry -> devicePaths.add(entry.getKey())); } } public static boolean isXMLOrJson(String fileDirPath, String libraryName){ String fileExtension = libraryName.substring(libraryName.lastIndexOf(".") + 1); - if(fileExtension.equalsIgnoreCase("xml") || + if (fileExtension.equalsIgnoreCase("xml") || fileExtension.equalsIgnoreCase("json")){ return true; } - System.out.println("The file " + fileDirPath + libraryName + " is not the right type of file."); + logger.warn("The file {}{} is not the right type of file.", fileDirPath, libraryName); return false; } diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/utilities/ResourceUtils.java b/tooling/src/main/java/org/opencds/cqf/tooling/utilities/ResourceUtils.java index d9b4a8d08..ec8442847 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/utilities/ResourceUtils.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/utilities/ResourceUtils.java @@ -7,12 +7,16 @@ import java.io.IOException; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import ca.uhn.fhir.util.TerserUtil; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.Validate; import org.cqframework.cql.cql2elm.CqlTranslator; @@ -24,6 +28,7 @@ import org.cqframework.cql.cql2elm.quick.FhirLibrarySourceProvider; import org.hl7.elm.r1.IncludeDef; import org.hl7.elm.r1.ValueSetDef; +import org.hl7.elm.r1.VersionedIdentifier; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IBaseElement; @@ -45,259 +50,255 @@ import ca.uhn.fhir.context.RuntimeCompositeDatatypeDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition; -public class ResourceUtils -{ - private static final Logger logger = LoggerFactory.getLogger(ResourceUtils.class); - - private static String cqfLibraryExtensionUrl = "http://hl7.org/fhir/StructureDefinition/cqf-library"; - - public enum FhirVersion - { - DSTU3("dstu3"), R4("r4"); - - private String string; - public String toString() - { - return this.string; - } - - private FhirVersion(String string) - { - this.string = string; - } - - public static FhirVersion parse(String value) { - switch (value) { - case "dstu3": - return DSTU3; - case "r4": - return R4; - default: - throw new RuntimeException("Unable to parse FHIR version value:" + value); - } - } - } - - public static String getId(String name, String version, boolean versioned) { - return name.replaceAll("_", "-") + (versioned ? "-" + version.replaceAll("_", ".") : ""); - } - - public static void setIgId(String baseId, IBaseResource resource, Boolean includeVersion) - { - String version = includeVersion ? resource.getMeta().getVersionId() : ""; +public class ResourceUtils { + private static final Logger logger = LoggerFactory.getLogger(ResourceUtils.class); + private static final String CQF_LIBRARY_EXT_URL = "http://hl7.org/fhir/StructureDefinition/cqf-library"; + + public enum FhirVersion { + DSTU3("dstu3"), R4("r4"); + + private String string; + public String toString() + { + return this.string; + } + + private FhirVersion(String string) + { + this.string = string; + } + + public static FhirVersion parse(String value) { + switch (value) { + case "dstu3": + return DSTU3; + case "r4": + return R4; + default: + throw new RuntimeException("Unable to parse FHIR version value:" + value); + } + } + } + + public static String getId(String name, String version, boolean versioned) { + return name.replace("_", "-") + (versioned ? "-" + version.replace("_", ".") : ""); + } + + public static void setIgId(String baseId, IBaseResource resource, Boolean includeVersion) + { + String version = Boolean.TRUE.equals(includeVersion) ? resource.getMeta().getVersionId() : ""; setIgId(baseId, resource, version); - } + } - public static void setIgId(String baseId, IBaseResource resource, String version) - { + public static void setIgId(String baseId, IBaseResource resource, String version) + { String igId = ""; String resourceName = resource.getClass().getSimpleName().toLowerCase(); String versionId = (version == null || version.equals("")) ? "" : "-" + version; if (resource instanceof org.hl7.fhir.dstu3.model.Bundle || resource instanceof org.hl7.fhir.r4.model.Bundle) { - igId = baseId + versionId + "-" + resourceName; + igId = baseId + versionId + "-" + resourceName; } else { - igId = resourceName + "-" + baseId + versionId; + igId = resourceName + "-" + baseId + versionId; } igId = igId.replace("_", "-"); resource.setId(igId); - } + } - public static FhirContext getFhirContext(FhirVersion fhirVersion) { + public static FhirContext getFhirContext(FhirVersion fhirVersion) { switch (fhirVersion) { - case DSTU3: - return FhirContext.forDstu3Cached(); - case R4: - return FhirContext.forR4Cached(); - default: - throw new IllegalArgumentException("Unsupported FHIR version: " + fhirVersion); + case DSTU3: + return FhirContext.forDstu3Cached(); + case R4: + return FhirContext.forR4Cached(); + default: + throw new IllegalArgumentException("Unsupported FHIR version: " + fhirVersion); } - } + } - private static List getStu3RelatedArtifacts(String pathToLibrary, FhirContext fhirContext) { + private static List getStu3RelatedArtifacts(String pathToLibrary, FhirContext fhirContext) { Object mainLibrary = IOUtils.readResource(pathToLibrary, fhirContext); if (!(mainLibrary instanceof org.hl7.fhir.dstu3.model.Library)) { - throw new IllegalArgumentException("pathToLibrary must be a path to a Library type Resource"); + throw new IllegalArgumentException("pathToLibrary must be a path to a Library type Resource"); } return ((org.hl7.fhir.dstu3.model.Library)mainLibrary).getRelatedArtifact(); - } + } - private static List getR4RelatedArtifacts(String pathToLibrary, FhirContext fhirContext) { + private static List getR4RelatedArtifacts(String pathToLibrary, FhirContext fhirContext) { Object mainLibrary = IOUtils.readResource(pathToLibrary, fhirContext); if (!(mainLibrary instanceof org.hl7.fhir.r4.model.Library)) { - throw new IllegalArgumentException("pathToLibrary must be a path to a Library type Resource"); + throw new IllegalArgumentException("pathToLibrary must be a path to a Library type Resource"); } return ((org.hl7.fhir.r4.model.Library)mainLibrary).getRelatedArtifact(); - } + } - public static Map getDepLibraryResources(String path, FhirContext fhirContext, Encoding encoding, Boolean versioned, Logger logger) { - Map dependencyLibraries = new HashMap(); + public static Map getDepLibraryResources(String path, FhirContext fhirContext, Encoding encoding, Boolean versioned, Logger logger) { + Map dependencyLibraries = new HashMap<>(); switch (fhirContext.getVersion().getVersion()) { - case DSTU3: + case DSTU3: return getStu3DepLibraryResources(path, dependencyLibraries, fhirContext, encoding, versioned); - case R4: + case R4: return getR4DepLibraryResources(path, dependencyLibraries, fhirContext, encoding, versioned, logger); - default: + default: throw new IllegalArgumentException("Unsupported fhir version: " + fhirContext.getVersion().getVersion().getFhirVersionString()); } - } + } - public static List getDepLibraryPaths(String path, FhirContext fhirContext, Encoding encoding, Boolean versioned) { + public static List getDepLibraryPaths(String path, FhirContext fhirContext, Encoding encoding, Boolean versioned) { switch (fhirContext.getVersion().getVersion()) { - case DSTU3: + case DSTU3: return getStu3DepLibraryPaths(path, fhirContext, encoding, versioned); - case R4: + case R4: return getR4DepLibraryPaths(path, fhirContext, encoding,versioned); - default: + default: throw new IllegalArgumentException("Unsupported fhir version: " + fhirContext.getVersion().getVersion().getFhirVersionString()); } - } + } - private static List getStu3DepLibraryPaths(String path, FhirContext fhirContext, Encoding encoding, Boolean versioned) { - List paths = new ArrayList(); + private static List getStu3DepLibraryPaths(String path, FhirContext fhirContext, Encoding encoding, Boolean versioned) { + List paths = new ArrayList<>(); String directoryPath = FilenameUtils.getFullPath(path); String fileName = FilenameUtils.getName(path); String prefix = fileName.toLowerCase().startsWith("library-") ? fileName.substring(0, 8) : ""; List relatedArtifacts = getStu3RelatedArtifacts(path, fhirContext); for (org.hl7.fhir.dstu3.model.RelatedArtifact relatedArtifact : relatedArtifacts) { - if (relatedArtifact.getType() == org.hl7.fhir.dstu3.model.RelatedArtifact.RelatedArtifactType.DEPENDSON) { + if (relatedArtifact.getType() == org.hl7.fhir.dstu3.model.RelatedArtifact.RelatedArtifactType.DEPENDSON) { if (relatedArtifact.getResource().getReference().contains("Library/")) { - String dependencyLibraryName; - // Issue 96 - Do not include version number in the filename - if (versioned) { - dependencyLibraryName = IOUtils.formatFileName(relatedArtifact.getResource().getReference().split("Library/")[1].replaceAll("\\|", "-"), encoding, fhirContext); - } else { - String name = relatedArtifact.getResource().getReference().split("Library/")[1]; - dependencyLibraryName = IOUtils.formatFileName(name.split("\\|")[0], encoding, fhirContext); - } - String dependencyLibraryPath = FilenameUtils.concat(directoryPath, prefix + dependencyLibraryName); - IOUtils.putAllInListIfAbsent(getStu3DepLibraryPaths(dependencyLibraryPath, fhirContext, encoding, versioned), paths); - IOUtils.putInListIfAbsent(dependencyLibraryPath, paths); - } - } + String dependencyLibraryName; + // Issue 96 - Do not include version number in the filename + if (Boolean.TRUE.equals(versioned)) { + dependencyLibraryName = IOUtils.formatFileName(relatedArtifact.getResource().getReference().split("Library/")[1].replace("\\|", "-"), encoding, fhirContext); + } else { + String name = relatedArtifact.getResource().getReference().split("Library/")[1]; + dependencyLibraryName = IOUtils.formatFileName(name.split("\\|")[0], encoding, fhirContext); + } + String dependencyLibraryPath = FilenameUtils.concat(directoryPath, prefix + dependencyLibraryName); + IOUtils.putAllInListIfAbsent(getStu3DepLibraryPaths(dependencyLibraryPath, fhirContext, encoding, versioned), paths); + IOUtils.putInListIfAbsent(dependencyLibraryPath, paths); + } + } } return paths; - } + } - private static Map getStu3DepLibraryResources(String path, Map dependencyLibraries, FhirContext fhirContext, Encoding encoding, Boolean versioned) { + private static Map getStu3DepLibraryResources(String path, Map dependencyLibraries, FhirContext fhirContext, Encoding encoding, Boolean versioned) { List dependencyLibraryPaths = getStu3DepLibraryPaths(path, fhirContext, encoding, versioned); for (String dependencyLibraryPath : dependencyLibraryPaths) { - Object resource = IOUtils.readResource(dependencyLibraryPath, fhirContext); - if (resource instanceof org.hl7.fhir.dstu3.model.Library) { - org.hl7.fhir.dstu3.model.Library library = (org.hl7.fhir.dstu3.model.Library)resource; - dependencyLibraries.putIfAbsent(library.getId(), library); - } + Object resource = IOUtils.readResource(dependencyLibraryPath, fhirContext); + if (resource instanceof org.hl7.fhir.dstu3.model.Library) { + org.hl7.fhir.dstu3.model.Library library = (org.hl7.fhir.dstu3.model.Library)resource; + dependencyLibraries.putIfAbsent(library.getId(), library); + } } return dependencyLibraries; - } + } - // if | exists there is a version - private static List getR4DepLibraryPaths(String path, FhirContext fhirContext, Encoding encoding, Boolean versioned) { - List paths = new ArrayList(); + // if | exists there is a version + private static List getR4DepLibraryPaths(String path, FhirContext fhirContext, Encoding encoding, Boolean versioned) { + List paths = new ArrayList<>(); String directoryPath = FilenameUtils.getFullPath(path); String fileName = FilenameUtils.getName(path); String prefix = fileName.toLowerCase().startsWith("library-") ? fileName.substring(0, 8) : ""; List relatedArtifacts = getR4RelatedArtifacts(path, fhirContext); for (org.hl7.fhir.r4.model.RelatedArtifact relatedArtifact : relatedArtifacts) { - if (relatedArtifact.getType() == org.hl7.fhir.r4.model.RelatedArtifact.RelatedArtifactType.DEPENDSON) { - if (relatedArtifact.getResource().contains("Library/")) { - String dependencyLibraryName; - // Issue 96 - Do not include version number in the filename - if (versioned) { - dependencyLibraryName = IOUtils.formatFileName(relatedArtifact.getResource().split("Library/")[1].replaceAll("\\|", "-"), encoding, fhirContext); - } else { - String name = relatedArtifact.getResource().split("Library/")[1]; - dependencyLibraryName = IOUtils.formatFileName(name.split("\\|")[0], encoding, fhirContext); - } - String dependencyLibraryPath = FilenameUtils.concat(directoryPath, prefix + dependencyLibraryName); - IOUtils.putInListIfAbsent(dependencyLibraryPath, paths); - } - } + if (relatedArtifact.getType() == org.hl7.fhir.r4.model.RelatedArtifact.RelatedArtifactType.DEPENDSON) { + if (relatedArtifact.getResource().contains("Library/")) { + String dependencyLibraryName; + // Issue 96 - Do not include version number in the filename + if (versioned) { + dependencyLibraryName = IOUtils.formatFileName(relatedArtifact.getResource().split("Library/")[1].replaceAll("\\|", "-"), encoding, fhirContext); + } else { + String name = relatedArtifact.getResource().split("Library/")[1]; + dependencyLibraryName = IOUtils.formatFileName(name.split("\\|")[0], encoding, fhirContext); + } + String dependencyLibraryPath = FilenameUtils.concat(directoryPath, prefix + dependencyLibraryName); + IOUtils.putInListIfAbsent(dependencyLibraryPath, paths); + } + } } return paths; - } + } - private static Map getR4DepLibraryResources(String path, Map dependencyLibraries, FhirContext fhirContext, Encoding encoding, Boolean versioned, Logger logger) { + private static Map getR4DepLibraryResources(String path, Map dependencyLibraries, FhirContext fhirContext, Encoding encoding, Boolean versioned, Logger logger) { List dependencyLibraryPaths = getR4DepLibraryPaths(path, fhirContext, encoding, versioned); for (String dependencyLibraryPath : dependencyLibraryPaths) { - if (dependencyLibraryPath.contains("ModelInfo")) { - logger.debug("skipping ModelInfo"); - } else { - Object resource = IOUtils.readResource(dependencyLibraryPath, fhirContext); - if (resource instanceof org.hl7.fhir.r4.model.Library) { - org.hl7.fhir.r4.model.Library library = (org.hl7.fhir.r4.model.Library)resource; - dependencyLibraries.putIfAbsent(library.getId(), library); - } - } + if (dependencyLibraryPath.contains("ModelInfo")) { + logger.debug("skipping ModelInfo"); + } else { + Object resource = IOUtils.readResource(dependencyLibraryPath, fhirContext); + if (resource instanceof org.hl7.fhir.r4.model.Library) { + org.hl7.fhir.r4.model.Library library = (org.hl7.fhir.r4.model.Library)resource; + dependencyLibraries.putIfAbsent(library.getId(), library); + } + } } return dependencyLibraries; - } - - public static List getStu3TerminologyDependencies(List relatedArtifacts) { - List urls = new ArrayList(); - for (org.hl7.fhir.dstu3.model.RelatedArtifact relatedArtifact : relatedArtifacts) { - if (relatedArtifact.hasType() && relatedArtifact.getType() == org.hl7.fhir.dstu3.model.RelatedArtifact.RelatedArtifactType.DEPENDSON) { - if (relatedArtifact.hasResource() && relatedArtifact.getResource().hasReference() - && (relatedArtifact.getResource().getReference().contains("CodeSystem/") || relatedArtifact.getResource().getReference().contains("ValueSet/"))) { - urls.add(relatedArtifact.getResource().getReference()); - } - } - } - return urls; - } - - public static List getR4TerminologyDependencies(List relatedArtifacts) { - List urls = new ArrayList(); - for (org.hl7.fhir.r4.model.RelatedArtifact relatedArtifact : relatedArtifacts) { - if (relatedArtifact.hasType() && relatedArtifact.getType() == org.hl7.fhir.r4.model.RelatedArtifact.RelatedArtifactType.DEPENDSON) { - if (relatedArtifact.hasResource() && (relatedArtifact.getResource().contains("CodeSystem/") || relatedArtifact.getResource().contains("ValueSet/"))) { - urls.add(relatedArtifact.getResource()); - } - } - } - return urls; - } - - public static List getTerminologyDependencies(IBaseResource resource, FhirContext fhirContext) throws Exception { - switch (fhirContext.getVersion().getVersion()) { - case DSTU3: - switch (resource.fhirType()) { - case "Library": { - return getStu3TerminologyDependencies(((org.hl7.fhir.dstu3.model.Library)resource).getRelatedArtifact()); - } - case "Measure": { - return getStu3TerminologyDependencies(((org.hl7.fhir.dstu3.model.Measure)resource).getRelatedArtifact()); - } - default: throw new IllegalArgumentException(String.format("Could not retrieve relatedArtifacts from %s", resource.fhirType())); - } - case R4: - switch (resource.fhirType()) { - case "Library": { - return getR4TerminologyDependencies(((org.hl7.fhir.r4.model.Library)resource).getRelatedArtifact()); - } - case "Measure": { - return getR4TerminologyDependencies(((org.hl7.fhir.r4.model.Measure)resource).getRelatedArtifact()); - } - default: throw new IllegalArgumentException(String.format("Could not retrieve relatedArtifacts from %s", resource.fhirType())); - } - default: - throw new IllegalArgumentException("Unsupported fhir version: " + fhirContext.getVersion().getVersion().getFhirVersionString()); - } - } + } - public static Map getDepValueSetResources(String cqlContentPath, String igPath, FhirContext fhirContext, boolean includeDependencies, Boolean includeVersion) throws Exception { - Map valueSetResources = new HashMap(); - List valueSetDefIDs = getDepELMValueSetDefIDs(cqlContentPath); - HashSet dependencies = new HashSet<>(); + public static List getStu3TerminologyDependencies(List relatedArtifacts) { + List urls = new ArrayList<>(); + for (org.hl7.fhir.dstu3.model.RelatedArtifact relatedArtifact : relatedArtifacts) { + if (relatedArtifact.hasType() && relatedArtifact.getType() == org.hl7.fhir.dstu3.model.RelatedArtifact.RelatedArtifactType.DEPENDSON) { + if (relatedArtifact.hasResource() && relatedArtifact.getResource().hasReference() + && (relatedArtifact.getResource().getReference().contains("CodeSystem/") || relatedArtifact.getResource().getReference().contains("ValueSet/"))) { + urls.add(relatedArtifact.getResource().getReference()); + } + } + } + return urls; + } + + public static List getR4TerminologyDependencies(List relatedArtifacts) { + List urls = new ArrayList<>(); + for (org.hl7.fhir.r4.model.RelatedArtifact relatedArtifact : relatedArtifacts) { + if (relatedArtifact.hasType() && relatedArtifact.getType() == org.hl7.fhir.r4.model.RelatedArtifact.RelatedArtifactType.DEPENDSON) { + if (relatedArtifact.hasResource() && (relatedArtifact.getResource().contains("CodeSystem/") || relatedArtifact.getResource().contains("ValueSet/"))) { + urls.add(relatedArtifact.getResource()); + } + } + } + return urls; + } + + public static List getTerminologyDependencies(IBaseResource resource, FhirContext fhirContext) { + switch (fhirContext.getVersion().getVersion()) { + case DSTU3: + switch (resource.fhirType()) { + case "Library": { + return getStu3TerminologyDependencies(((org.hl7.fhir.dstu3.model.Library)resource).getRelatedArtifact()); + } + case "Measure": { + return getStu3TerminologyDependencies(((org.hl7.fhir.dstu3.model.Measure)resource).getRelatedArtifact()); + } + default: throw new IllegalArgumentException(String.format("Could not retrieve relatedArtifacts from %s", resource.fhirType())); + } + case R4: + switch (resource.fhirType()) { + case "Library": { + return getR4TerminologyDependencies(((org.hl7.fhir.r4.model.Library)resource).getRelatedArtifact()); + } + case "Measure": { + return getR4TerminologyDependencies(((org.hl7.fhir.r4.model.Measure)resource).getRelatedArtifact()); + } + default: throw new IllegalArgumentException(String.format("Could not retrieve relatedArtifacts from %s", resource.fhirType())); + } + default: + throw new IllegalArgumentException("Unsupported fhir version: " + fhirContext.getVersion().getVersion().getFhirVersionString()); + } + } + + public static Map getDepValueSetResources(String cqlContentPath, String igPath, FhirContext fhirContext, boolean includeDependencies, Boolean includeVersion) throws Exception { + Map valueSetResources = new HashMap<>(); + List valueSetDefIDs = getDepELMValueSetDefIDs(cqlContentPath); for (String valueSetUrl : valueSetDefIDs) { - ValueSetsProcessor.getCachedValueSets(fhirContext).entrySet().stream() - .filter(entry -> entry.getKey().equals(valueSetUrl)) - .forEach(entry -> valueSetResources.put(entry.getKey(), entry.getValue())); + ValueSetsProcessor.getCachedValueSets(fhirContext).entrySet().stream() + .filter(entry -> entry.getKey().equals(valueSetUrl)) + .forEach(entry -> valueSetResources.put(entry.getKey(), entry.getValue())); } - dependencies.addAll(valueSetDefIDs); + Set dependencies = new HashSet<>(valueSetDefIDs); if (includeDependencies) { List dependencyCqlPaths = IOUtils.getDependencyCqlPaths(cqlContentPath, includeVersion); @@ -305,102 +306,98 @@ public static Map getDepValueSetResources(String cqlConte Map dependencyValueSets = getDepValueSetResources(path, igPath, fhirContext, includeDependencies, includeVersion); dependencies.addAll(dependencyValueSets.keySet()); for (Entry entry : dependencyValueSets.entrySet()) { - valueSetResources.putIfAbsent(entry.getKey(), entry.getValue()); + valueSetResources.putIfAbsent(entry.getKey(), entry.getValue()); } } } if (dependencies.size() != valueSetResources.size()) { - String message = (dependencies.size() - valueSetResources.size()) + " missing ValueSets: \r\n"; - dependencies.removeAll(valueSetResources.keySet()); - for (String valueSetUrl : dependencies) { - message += valueSetUrl + " MISSING \r\n"; - } - System.out.println(message); - throw new Exception(message); + String message = (dependencies.size() - valueSetResources.size()) + " missing ValueSets: \r\n"; + dependencies.removeAll(valueSetResources.keySet()); + for (String valueSetUrl : dependencies) { + message += valueSetUrl + " MISSING \r\n"; + } + System.out.println(message); + throw new Exception(message); } return valueSetResources; - } + } - public static ArrayList getIncludedLibraryNames(String cqlContentPath, Boolean includeVersion) { - ArrayList includedLibraryNames = new ArrayList(); - ArrayList includedDefs = getIncludedDefs(cqlContentPath); + public static List getIncludedLibraryNames(String cqlContentPath, Boolean includeVersion) { + List includedLibraryNames = new ArrayList<>(); + List includedDefs = getIncludedDefs(cqlContentPath); for (IncludeDef def : includedDefs) { - //TODO: replace true with versioned variable - IOUtils.putInListIfAbsent(getId(def.getPath(), def.getVersion(), includeVersion), includedLibraryNames); + //TODO: replace true with versioned variable + IOUtils.putInListIfAbsent(getId(def.getPath(), def.getVersion(), includeVersion), includedLibraryNames); } return includedLibraryNames; - } + } - public static ArrayList getDepELMValueSetDefIDs(String cqlContentPath) { - ArrayList includedValueSetDefIDs = new ArrayList(); - ArrayList valueSetDefs = getValueSetDefs(cqlContentPath); + public static List getDepELMValueSetDefIDs(String cqlContentPath) { + List includedValueSetDefIDs = new ArrayList<>(); + List valueSetDefs = getValueSetDefs(cqlContentPath); for (ValueSetDef def : valueSetDefs) { - IOUtils.putInListIfAbsent(def.getId(), includedValueSetDefIDs); + IOUtils.putInListIfAbsent(def.getId(), includedValueSetDefIDs); } return includedValueSetDefIDs; - } + } - public static ArrayList getIncludedDefs(String cqlContentPath) { - ArrayList includedDefs = new ArrayList(); + public static List getIncludedDefs(String cqlContentPath) { + ArrayList includedDefs = new ArrayList<>(); org.hl7.elm.r1.Library elm; try { - elm = getElmFromCql(cqlContentPath); + elm = getElmFromCql(cqlContentPath); } catch (Exception e) { - System.out.println("error processing cql: "); - System.out.println(e.getMessage()); - return includedDefs; + System.out.println("error processing cql: "); + System.out.println(e.getMessage()); + return includedDefs; } if (elm.getIncludes() != null && !elm.getIncludes().getDef().isEmpty()) { - for (IncludeDef def : elm.getIncludes().getDef()) { - includedDefs.add(def); - } + includedDefs.addAll(elm.getIncludes().getDef()); } return includedDefs; - } + } - public static ArrayList getValueSetDefs(String cqlContentPath) { - ArrayList valueSetDefs = new ArrayList(); + public static List getValueSetDefs(String cqlContentPath) { + ArrayList valueSetDefs = new ArrayList<>(); org.hl7.elm.r1.Library elm; try { - elm = getElmFromCql(cqlContentPath); + elm = getElmFromCql(cqlContentPath); } catch (Exception e) { - System.out.println("error translating cql: "); - return valueSetDefs; + System.out.println("error translating cql: "); + return valueSetDefs; } if (elm.getValueSets() != null && !elm.getValueSets().getDef().isEmpty()) { - for (ValueSetDef def : elm.getValueSets().getDef()) { - valueSetDefs.add(def); - } + valueSetDefs.addAll(elm.getValueSets().getDef()); } return valueSetDefs; - } + } - public static CqlTranslatorOptions getTranslatorOptions(String folder) { + public static CqlTranslatorOptions getTranslatorOptions(String folder) { String optionsFileName = folder + File.separator + "cql-options.json"; - CqlTranslatorOptions options = null; + CqlTranslatorOptions options; File file = new File(optionsFileName); if (file.exists()) { - options = CqlTranslatorOptionsMapper.fromFile(file.getAbsolutePath()); - logger.debug("cql-options loaded from: {}", file.getAbsolutePath()); + options = CqlTranslatorOptionsMapper.fromFile(file.getAbsolutePath()); + logger.debug("cql-options loaded from: {}", file.getAbsolutePath()); } else { - options = CqlTranslatorOptions.defaultOptions(); - if (!options.getFormats().contains(CqlTranslator.Format.XML)) { - options.getFormats().add(CqlTranslator.Format.XML); - } - logger.debug("cql-options not found. Using default options."); + options = CqlTranslatorOptions.defaultOptions(); + if (!options.getFormats().contains(CqlTranslator.Format.XML)) { + options.getFormats().add(CqlTranslator.Format.XML); + } + logger.debug("cql-options not found. Using default options."); } return options; - } + } - private static Map cachedElm = new HashMap(); - public static org.hl7.elm.r1.Library getElmFromCql(String cqlContentPath) { + private static final Map cachedElm = new HashMap<>(); + public static org.hl7.elm.r1.Library getElmFromCql(String cqlContentPath) { org.hl7.elm.r1.Library elm = cachedElm.get(cqlContentPath); if (elm != null) { - return elm; + return elm; } String folder = IOUtils.getParentDirectoryPath(cqlContentPath); @@ -413,7 +410,7 @@ public static org.hl7.elm.r1.Library getElmFromCql(String cqlContentPath) { ModelManager modelManager = new ModelManager(); LibraryManager libraryManager = new LibraryManager(modelManager); // if (packages != null) { - // libraryManager.getLibrarySourceLoader().registerProvider(new NpmLibrarySourceProvider(packages, reader, logger)); + // libraryManager.getLibrarySourceLoader().registerProvider(new NpmLibrarySourceProvider(packages, reader, logger)); // } libraryManager.getLibrarySourceLoader().registerProvider(new FhirLibrarySourceProvider()); libraryManager.getLibrarySourceLoader().registerProvider(new DefaultLibrarySourceProvider(Paths.get(folder))); @@ -435,412 +432,444 @@ public static org.hl7.elm.r1.Library getElmFromCql(String cqlContentPath) { elm = translator.toELM(); cachedElm.put(cqlContentPath, elm); return elm; - } + } - public static Boolean safeAddResource(String path, Map resources, FhirContext fhirContext) { - Boolean added = true; + public static Boolean safeAddResource(String path, Map resources, FhirContext fhirContext) { + boolean added = true; try { - IBaseResource resource = IOUtils.readResource(path, fhirContext, true); - if (resource != null) { + IBaseResource resource = IOUtils.readResource(path, fhirContext, true); + if (resource != null) { // if(resources.containsKey(resource.getIdElement().getIdPart())){ // IBaseResource storedResource = resources.get(resource.getIdElement().getIdPart()); // } - resources.putIfAbsent(resource.fhirType() + "/" + resource.getIdElement().getIdPart(), resource); - } else { + resources.putIfAbsent(resource.fhirType() + "/" + resource.getIdElement().getIdPart(), resource); + } else { added = false; LogUtils.putException(path, new Exception("Unable to add Resource: " + path)); - } + } } catch(Exception e) { - added = false; - LogUtils.putException(path, e); + added = false; + LogUtils.putException(path, e); } return added; - } + } - public static String getUrl(IBaseResource resource, FhirContext fhirContext) { + public static String getUrl(IBaseResource resource, FhirContext fhirContext) { switch (fhirContext.getVersion().getVersion()) { - case DSTU3: { - if (resource instanceof org.hl7.fhir.dstu3.model.Measure) { - return ((org.hl7.fhir.dstu3.model.Measure)resource).getUrl(); - } - if (resource instanceof org.hl7.fhir.dstu3.model.Library) { - return ((org.hl7.fhir.dstu3.model.Library)resource).getUrl(); - } - if (resource instanceof org.hl7.fhir.dstu3.model.PlanDefinition) { - return ((org.hl7.fhir.dstu3.model.PlanDefinition)resource).getUrl(); - } - if (resource instanceof org.hl7.fhir.dstu3.model.CodeSystem) { - return ((org.hl7.fhir.dstu3.model.CodeSystem)resource).getUrl(); - } - if (resource instanceof org.hl7.fhir.dstu3.model.ValueSet) { - return ((org.hl7.fhir.dstu3.model.ValueSet)resource).getUrl(); - } - if (resource instanceof org.hl7.fhir.dstu3.model.ActivityDefinition) { - return ((org.hl7.fhir.dstu3.model.ActivityDefinition)resource).getUrl(); - } - if (resource instanceof org.hl7.fhir.dstu3.model.StructureDefinition) { - return ((org.hl7.fhir.dstu3.model.StructureDefinition)resource).getUrl(); - } - if (resource instanceof org.hl7.fhir.dstu3.model.GraphDefinition) { - return ((org.hl7.fhir.dstu3.model.GraphDefinition)resource).getUrl(); - } - throw new IllegalArgumentException(String.format("Could not retrieve url for resource type %s", resource.fhirType())); - } - case R4: { - if (resource instanceof org.hl7.fhir.r4.model.Measure) { - return ((org.hl7.fhir.r4.model.Measure)resource).getUrl(); - } - if (resource instanceof org.hl7.fhir.r4.model.Library) { - return ((org.hl7.fhir.r4.model.Library)resource).getUrl(); - } - if (resource instanceof org.hl7.fhir.r4.model.PlanDefinition) { - return ((org.hl7.fhir.r4.model.PlanDefinition)resource).getUrl(); - } - if (resource instanceof org.hl7.fhir.r4.model.CodeSystem) { - return ((org.hl7.fhir.r4.model.CodeSystem)resource).getUrl(); - } - if (resource instanceof org.hl7.fhir.r4.model.ValueSet) { - return ((org.hl7.fhir.r4.model.ValueSet)resource).getUrl(); - } - if (resource instanceof org.hl7.fhir.r4.model.ActivityDefinition) { - return ((org.hl7.fhir.r4.model.ActivityDefinition)resource).getUrl(); - } - if (resource instanceof org.hl7.fhir.r4.model.StructureDefinition) { - return ((org.hl7.fhir.r4.model.StructureDefinition)resource).getUrl(); - } - if (resource instanceof org.hl7.fhir.r4.model.GraphDefinition) { - return ((org.hl7.fhir.r4.model.GraphDefinition)resource).getUrl(); - } - throw new IllegalArgumentException(String.format("Could not retrieve url for resource type %s", resource.fhirType())); - } - default: - throw new IllegalArgumentException("Unsupported fhir version: " + fhirContext.getVersion().getVersion().getFhirVersionString()); - } - } - - public static String getName(IBaseResource resource, FhirContext fhirContext) { - switch (fhirContext.getVersion().getVersion()) { - case DSTU3: { - if (resource instanceof org.hl7.fhir.dstu3.model.Measure) { - return ((org.hl7.fhir.dstu3.model.Measure)resource).getName(); - } - if (resource instanceof org.hl7.fhir.dstu3.model.Library) { - return ((org.hl7.fhir.dstu3.model.Library)resource).getName(); - } - if (resource instanceof org.hl7.fhir.dstu3.model.PlanDefinition) { - return ((org.hl7.fhir.dstu3.model.PlanDefinition)resource).getName(); - } - if (resource instanceof org.hl7.fhir.dstu3.model.CodeSystem) { - return ((org.hl7.fhir.dstu3.model.CodeSystem)resource).getName(); - } - if (resource instanceof org.hl7.fhir.dstu3.model.ValueSet) { - return ((org.hl7.fhir.dstu3.model.ValueSet)resource).getName(); - } - if (resource instanceof org.hl7.fhir.dstu3.model.ActivityDefinition) { - return ((org.hl7.fhir.dstu3.model.ActivityDefinition)resource).getName(); - } - if (resource instanceof org.hl7.fhir.dstu3.model.StructureDefinition) { - return ((org.hl7.fhir.dstu3.model.StructureDefinition)resource).getName(); - } - if (resource instanceof org.hl7.fhir.dstu3.model.GraphDefinition) { - return ((org.hl7.fhir.dstu3.model.GraphDefinition)resource).getName(); - } - throw new IllegalArgumentException(String.format("Could not retrieve name for resource type %s", resource.fhirType())); - } - case R4: { - if (resource instanceof org.hl7.fhir.r4.model.Measure) { - return ((org.hl7.fhir.r4.model.Measure)resource).getName(); - } - if (resource instanceof org.hl7.fhir.r4.model.Library) { - return ((org.hl7.fhir.r4.model.Library)resource).getName(); - } - if (resource instanceof org.hl7.fhir.r4.model.PlanDefinition) { - return ((org.hl7.fhir.r4.model.PlanDefinition)resource).getName(); - } - if (resource instanceof org.hl7.fhir.r4.model.CodeSystem) { - return ((org.hl7.fhir.r4.model.CodeSystem)resource).getName(); - } - if (resource instanceof org.hl7.fhir.r4.model.ValueSet) { - return ((org.hl7.fhir.r4.model.ValueSet)resource).getName(); - } - if (resource instanceof org.hl7.fhir.r4.model.ActivityDefinition) { - return ((org.hl7.fhir.r4.model.ActivityDefinition)resource).getName(); - } - if (resource instanceof org.hl7.fhir.r4.model.StructureDefinition) { - return ((org.hl7.fhir.r4.model.StructureDefinition)resource).getName(); - } - if (resource instanceof org.hl7.fhir.r4.model.GraphDefinition) { - return ((org.hl7.fhir.r4.model.GraphDefinition)resource).getName(); - } - throw new IllegalArgumentException(String.format("Could not retrieve name for resource type %s", resource.fhirType())); + case DSTU3: { + if (resource instanceof org.hl7.fhir.dstu3.model.Measure) { + return ((org.hl7.fhir.dstu3.model.Measure)resource).getUrl(); } - default: - throw new IllegalArgumentException("Unsupported fhir version: " + fhirContext.getVersion().getVersion().getFhirVersionString()); - } - } + if (resource instanceof org.hl7.fhir.dstu3.model.Library) { + return ((org.hl7.fhir.dstu3.model.Library)resource).getUrl(); + } + if (resource instanceof org.hl7.fhir.dstu3.model.PlanDefinition) { + return ((org.hl7.fhir.dstu3.model.PlanDefinition)resource).getUrl(); + } + if (resource instanceof org.hl7.fhir.dstu3.model.CodeSystem) { + return ((org.hl7.fhir.dstu3.model.CodeSystem)resource).getUrl(); + } + if (resource instanceof org.hl7.fhir.dstu3.model.ValueSet) { + return ((org.hl7.fhir.dstu3.model.ValueSet)resource).getUrl(); + } + if (resource instanceof org.hl7.fhir.dstu3.model.ActivityDefinition) { + return ((org.hl7.fhir.dstu3.model.ActivityDefinition)resource).getUrl(); + } + if (resource instanceof org.hl7.fhir.dstu3.model.StructureDefinition) { + return ((org.hl7.fhir.dstu3.model.StructureDefinition)resource).getUrl(); + } + if (resource instanceof org.hl7.fhir.dstu3.model.GraphDefinition) { + return ((org.hl7.fhir.dstu3.model.GraphDefinition)resource).getUrl(); + } + throw new IllegalArgumentException(String.format("Could not retrieve url for resource type %s", resource.fhirType())); + } + case R4: { + if (resource instanceof org.hl7.fhir.r4.model.Measure) { + return ((org.hl7.fhir.r4.model.Measure)resource).getUrl(); + } + if (resource instanceof org.hl7.fhir.r4.model.Library) { + return ((org.hl7.fhir.r4.model.Library)resource).getUrl(); + } + if (resource instanceof org.hl7.fhir.r4.model.PlanDefinition) { + return ((org.hl7.fhir.r4.model.PlanDefinition)resource).getUrl(); + } + if (resource instanceof org.hl7.fhir.r4.model.CodeSystem) { + return ((org.hl7.fhir.r4.model.CodeSystem)resource).getUrl(); + } + if (resource instanceof org.hl7.fhir.r4.model.ValueSet) { + return ((org.hl7.fhir.r4.model.ValueSet)resource).getUrl(); + } + if (resource instanceof org.hl7.fhir.r4.model.ActivityDefinition) { + return ((org.hl7.fhir.r4.model.ActivityDefinition)resource).getUrl(); + } + if (resource instanceof org.hl7.fhir.r4.model.StructureDefinition) { + return ((org.hl7.fhir.r4.model.StructureDefinition)resource).getUrl(); + } + if (resource instanceof org.hl7.fhir.r4.model.GraphDefinition) { + return ((org.hl7.fhir.r4.model.GraphDefinition)resource).getUrl(); + } + throw new IllegalArgumentException(String.format("Could not retrieve url for resource type %s", resource.fhirType())); + } + default: + throw new IllegalArgumentException("Unsupported fhir version: " + fhirContext.getVersion().getVersion().getFhirVersionString()); + } + } + + public static String getVersion(IBaseResource resource, FhirContext fhirContext) { + IBase version = TerserUtil.getValueFirstRep(fhirContext, resource, "version"); + if (version instanceof IPrimitiveType) { + return ((IPrimitiveType) version).getValueAsString(); + } + else return null; + } + + public static String getName(IBaseResource resource, FhirContext fhirContext) { + switch (fhirContext.getVersion().getVersion()) { + case DSTU3: { + if (resource instanceof org.hl7.fhir.dstu3.model.Measure) { + return ((org.hl7.fhir.dstu3.model.Measure)resource).getName(); + } + if (resource instanceof org.hl7.fhir.dstu3.model.Library) { + return ((org.hl7.fhir.dstu3.model.Library)resource).getName(); + } + if (resource instanceof org.hl7.fhir.dstu3.model.PlanDefinition) { + return ((org.hl7.fhir.dstu3.model.PlanDefinition)resource).getName(); + } + if (resource instanceof org.hl7.fhir.dstu3.model.CodeSystem) { + return ((org.hl7.fhir.dstu3.model.CodeSystem)resource).getName(); + } + if (resource instanceof org.hl7.fhir.dstu3.model.ValueSet) { + return ((org.hl7.fhir.dstu3.model.ValueSet)resource).getName(); + } + if (resource instanceof org.hl7.fhir.dstu3.model.ActivityDefinition) { + return ((org.hl7.fhir.dstu3.model.ActivityDefinition)resource).getName(); + } + if (resource instanceof org.hl7.fhir.dstu3.model.StructureDefinition) { + return ((org.hl7.fhir.dstu3.model.StructureDefinition)resource).getName(); + } + if (resource instanceof org.hl7.fhir.dstu3.model.GraphDefinition) { + return ((org.hl7.fhir.dstu3.model.GraphDefinition)resource).getName(); + } + throw new IllegalArgumentException(String.format("Could not retrieve name for resource type %s", resource.fhirType())); + } + case R4: { + if (resource instanceof org.hl7.fhir.r4.model.Measure) { + return ((org.hl7.fhir.r4.model.Measure)resource).getName(); + } + if (resource instanceof org.hl7.fhir.r4.model.Library) { + return ((org.hl7.fhir.r4.model.Library)resource).getName(); + } + if (resource instanceof org.hl7.fhir.r4.model.PlanDefinition) { + return ((org.hl7.fhir.r4.model.PlanDefinition)resource).getName(); + } + if (resource instanceof org.hl7.fhir.r4.model.CodeSystem) { + return ((org.hl7.fhir.r4.model.CodeSystem)resource).getName(); + } + if (resource instanceof org.hl7.fhir.r4.model.ValueSet) { + return ((org.hl7.fhir.r4.model.ValueSet)resource).getName(); + } + if (resource instanceof org.hl7.fhir.r4.model.ActivityDefinition) { + return ((org.hl7.fhir.r4.model.ActivityDefinition)resource).getName(); + } + if (resource instanceof org.hl7.fhir.r4.model.StructureDefinition) { + return ((org.hl7.fhir.r4.model.StructureDefinition)resource).getName(); + } + if (resource instanceof org.hl7.fhir.r4.model.GraphDefinition) { + return ((org.hl7.fhir.r4.model.GraphDefinition)resource).getName(); + } + throw new IllegalArgumentException(String.format("Could not retrieve name for resource type %s", resource.fhirType())); + } + default: + throw new IllegalArgumentException("Unsupported fhir version: " + fhirContext.getVersion().getVersion().getFhirVersionString()); + } + } + + public static String getPrimaryLibraryUrl(IBaseResource resource, FhirContext fhirContext) { + if (resource instanceof org.hl7.fhir.r5.model.Measure) { + org.hl7.fhir.r5.model.Measure measure = (org.hl7.fhir.r5.model.Measure)resource; + if (!measure.hasLibrary() || measure.getLibrary().size() != 1) { + throw new IllegalArgumentException("Measure is expected to have one and only one library"); + } + return measure.getLibrary().get(0).getValue(); + } + else if (resource instanceof org.hl7.fhir.r4.model.Measure) { + org.hl7.fhir.r4.model.Measure measure = (org.hl7.fhir.r4.model.Measure)resource; + if (!measure.hasLibrary() || measure.getLibrary().size() != 1) { + throw new IllegalArgumentException("Measure is expected to have one and only one library"); + } + return measure.getLibrary().get(0).getValue(); + } + else if (resource instanceof org.hl7.fhir.dstu3.model.Measure) { + org.hl7.fhir.dstu3.model.Measure measure = (org.hl7.fhir.dstu3.model.Measure)resource; + if (!measure.hasLibrary() || measure.getLibrary().size() != 1) { + throw new IllegalArgumentException("Measure is expected to have one and only one library"); + } + String reference = measure.getLibrary().get(0).getReference(); + String[] parts = reference.split("/"); + return parts[parts.length - 1]; + } + else if (resource instanceof org.hl7.fhir.r5.model.PlanDefinition) { + org.hl7.fhir.r5.model.PlanDefinition planDefinition = (org.hl7.fhir.r5.model.PlanDefinition)resource; + if (!planDefinition.hasLibrary() || planDefinition.getLibrary().size() != 1) { + throw new IllegalArgumentException("PlanDefinition is expected to have one and only one library"); + } + return planDefinition.getLibrary().get(0).getValue(); + } + else if (resource instanceof org.hl7.fhir.r4.model.PlanDefinition) { + org.hl7.fhir.r4.model.PlanDefinition planDefinition = (org.hl7.fhir.r4.model.PlanDefinition)resource; + if (!planDefinition.hasLibrary() || planDefinition.getLibrary().size() != 1) { + throw new IllegalArgumentException("PlanDefinition is expected to have one and only one library"); + } + return planDefinition.getLibrary().get(0).getValue(); + } + else if (resource instanceof org.hl7.fhir.dstu3.model.PlanDefinition) { + org.hl7.fhir.dstu3.model.PlanDefinition planDefinition = (org.hl7.fhir.dstu3.model.PlanDefinition)resource; + if (!planDefinition.hasLibrary() || planDefinition.getLibrary().size() != 1) { + throw new IllegalArgumentException("PlanDefinition is expected to have one and only one library"); + } + String reference = planDefinition.getLibrary().get(0).getReference(); + String[] parts = reference.split("/"); + return parts[parts.length - 1]; + } + else if (resource instanceof org.hl7.fhir.r5.model.Questionnaire) { + org.hl7.fhir.r5.model.Questionnaire questionnaire = (org.hl7.fhir.r5.model.Questionnaire)resource; + + org.hl7.fhir.r5.model.Extension libraryExtension = questionnaire.getExtensionByUrl(CQF_LIBRARY_EXT_URL); + if (libraryExtension != null) { + return ((org.hl7.fhir.r5.model.CanonicalType)libraryExtension.getValue()).getValueAsString(); + } + + return null; + } + else if (resource instanceof org.hl7.fhir.r4.model.Questionnaire) { + org.hl7.fhir.r4.model.Questionnaire questionnaire = (org.hl7.fhir.r4.model.Questionnaire)resource; + + org.hl7.fhir.r4.model.Extension libraryExtension = questionnaire.getExtensionByUrl(CQF_LIBRARY_EXT_URL); + if (libraryExtension != null) { + return ((CanonicalType)libraryExtension.getValue()).getValueAsString(); + } - public static String getPrimaryLibraryUrl(IBaseResource resource, FhirContext fhirContext) { - if (resource instanceof org.hl7.fhir.r5.model.Measure) { - org.hl7.fhir.r5.model.Measure measure = (org.hl7.fhir.r5.model.Measure)resource; + return null; + } + else if (resource instanceof org.hl7.fhir.dstu3.model.Questionnaire) { + org.hl7.fhir.dstu3.model.Questionnaire questionnaire = (org.hl7.fhir.dstu3.model.Questionnaire)resource; + + List libraryExtensions = + questionnaire.getExtensionsByUrl(CQF_LIBRARY_EXT_URL); + + if (libraryExtensions.isEmpty()) { + return null; + } else { + Validate.isTrue(libraryExtensions.size() == 1, "Url " + CQF_LIBRARY_EXT_URL + " must have only one match"); + return libraryExtensions.get(0).getValue().toString(); + } + } + else { + throw new IllegalArgumentException("Unsupported fhir version: " + fhirContext.getVersion().getVersion().getFhirVersionString()); + } + } + + public static String getPrimaryLibraryName(IBaseResource resource, FhirContext fhirContext) { + switch (fhirContext.getVersion().getVersion()) { + case DSTU3: { + org.hl7.fhir.dstu3.model.Measure measure = (org.hl7.fhir.dstu3.model.Measure)resource; if (!measure.hasLibrary() || measure.getLibrary().size() != 1) { - throw new IllegalArgumentException("Measure is expected to have one and only one library"); + throw new IllegalArgumentException("Measure is expected to have one and only one library"); } - return measure.getLibrary().get(0).getValue(); - } - else if (resource instanceof org.hl7.fhir.r4.model.Measure) { + return getTail(measure.getLibrary().get(0).getReference()); + } + case R4: { org.hl7.fhir.r4.model.Measure measure = (org.hl7.fhir.r4.model.Measure)resource; if (!measure.hasLibrary() || measure.getLibrary().size() != 1) { - throw new IllegalArgumentException("Measure is expected to have one and only one library"); + throw new IllegalArgumentException("Measure is expected to have one and only one library"); } - return measure.getLibrary().get(0).getValue(); - } - else if (resource instanceof org.hl7.fhir.dstu3.model.Measure) { - org.hl7.fhir.dstu3.model.Measure measure = (org.hl7.fhir.dstu3.model.Measure)resource; - if (!measure.hasLibrary() || measure.getLibrary().size() != 1) { - throw new IllegalArgumentException("Measure is expected to have one and only one library"); - } - String reference = measure.getLibrary().get(0).getReference(); - String parts[] = reference.split("/"); - return parts[parts.length - 1]; - } - else if (resource instanceof org.hl7.fhir.r5.model.PlanDefinition) { - org.hl7.fhir.r5.model.PlanDefinition planDefinition = (org.hl7.fhir.r5.model.PlanDefinition)resource; - if (!planDefinition.hasLibrary() || planDefinition.getLibrary().size() != 1) { - throw new IllegalArgumentException("PlanDefinition is expected to have one and only one library"); - } - return planDefinition.getLibrary().get(0).getValue(); - } - else if (resource instanceof org.hl7.fhir.r4.model.PlanDefinition) { - org.hl7.fhir.r4.model.PlanDefinition planDefinition = (org.hl7.fhir.r4.model.PlanDefinition)resource; - if (!planDefinition.hasLibrary() || planDefinition.getLibrary().size() != 1) { - throw new IllegalArgumentException("PlanDefinition is expected to have one and only one library"); - } - return planDefinition.getLibrary().get(0).getValue(); - } - else if (resource instanceof org.hl7.fhir.dstu3.model.PlanDefinition) { - org.hl7.fhir.dstu3.model.PlanDefinition planDefinition = (org.hl7.fhir.dstu3.model.PlanDefinition)resource; - if (!planDefinition.hasLibrary() || planDefinition.getLibrary().size() != 1) { - throw new IllegalArgumentException("PlanDefinition is expected to have one and only one library"); - } - String reference = planDefinition.getLibrary().get(0).getReference(); - String parts[] = reference.split("/"); - return parts[parts.length - 1]; - } - else if (resource instanceof org.hl7.fhir.r5.model.Questionnaire) { - org.hl7.fhir.r5.model.Questionnaire questionnaire = (org.hl7.fhir.r5.model.Questionnaire)resource; - - org.hl7.fhir.r5.model.Extension libraryExtension = questionnaire.getExtensionByUrl(cqfLibraryExtensionUrl); - if (libraryExtension != null) { - return ((org.hl7.fhir.r5.model.CanonicalType)libraryExtension.getValue()).getValueAsString(); + return getTail(measure.getLibrary().get(0).getValue()); + } + default: + throw new IllegalArgumentException("Unsupported fhir version: " + fhirContext.getVersion().getVersion().getFhirVersionString()); + } + } + + public static VersionedIdentifier getIdentifier(IBaseResource resource, FhirContext fhirContext) { + String url = getUrl(resource, fhirContext); + return CanonicalUtils.toVersionedIdentifierAnyResource(url).withVersion(getVersion(resource, fhirContext)); + } + + public static boolean compareResourcePrimitiveElements(IBaseResource res1, IBaseResource res2, + FhirContext fhirContext, String... elements) { + AtomicBoolean match = new AtomicBoolean(true); + if (res1 != null && res2 != null && res1.fhirType().equals(res2.fhirType())) { + Arrays.stream(elements).forEach(element -> { + IBase e1 = TerserUtil.getValueFirstRep(fhirContext, res1, element); + IBase e2 = TerserUtil.getValueFirstRep(fhirContext, res2, element); + if (e1 instanceof IPrimitiveType && e2 instanceof IPrimitiveType + && !((IPrimitiveType) e1).getValueAsString().equals(((IPrimitiveType) e2).getValueAsString())) { + match.set(false); } + }); + } + return match.get(); + } - return null; - } - else if (resource instanceof org.hl7.fhir.r4.model.Questionnaire) { - org.hl7.fhir.r4.model.Questionnaire questionnaire = (org.hl7.fhir.r4.model.Questionnaire)resource; + public static boolean compareResourceIdUrlAndVersion(IBaseResource res1, IBaseResource res2, + FhirContext fhirContext) { + return compareResourcePrimitiveElements(res1, res2, fhirContext, "id", "url", "version"); + } - org.hl7.fhir.r4.model.Extension libraryExtension = questionnaire.getExtensionByUrl(cqfLibraryExtensionUrl); - if (libraryExtension != null) { - return ((CanonicalType)libraryExtension.getValue()).getValueAsString(); - } + public static Map getActivityDefinitionResources(String planDefinitionPath, FhirContext fhirContext, Boolean includeVersion) { + Map activityDefinitions = new HashMap<>(); + IBaseResource planDefinition = IOUtils.readResource(planDefinitionPath, fhirContext, true); + Object actionChild = resolveProperty(planDefinition, "action", fhirContext); - return null; - } - else if (resource instanceof org.hl7.fhir.dstu3.model.Questionnaire) { - org.hl7.fhir.dstu3.model.Questionnaire questionnaire = (org.hl7.fhir.dstu3.model.Questionnaire)resource; - - List libraryExtensions = - questionnaire.getExtensionsByUrl(cqfLibraryExtensionUrl); - - if (libraryExtensions.size() == 0) { - return null; - } else { - Validate.isTrue(libraryExtensions.size() == 1, "Url " + cqfLibraryExtensionUrl + " must have only one match", new Object[0]); - return libraryExtensions.get(0).getValue().toString(); - } - } - else { - throw new IllegalArgumentException("Unsupported fhir version: " + fhirContext.getVersion().getVersion().getFhirVersionString()); - } - } - - public static String getPrimaryLibraryName(IBaseResource resource, FhirContext fhirContext) { - switch (fhirContext.getVersion().getVersion()) { - case DSTU3: { - org.hl7.fhir.dstu3.model.Measure measure = (org.hl7.fhir.dstu3.model.Measure)resource; - if (!measure.hasLibrary() || measure.getLibrary().size() != 1) { - throw new IllegalArgumentException("Measure is expected to have one and only one library"); - } - return getTail(measure.getLibrary().get(0).getReference()); - } - case R4: { - org.hl7.fhir.r4.model.Measure measure = (org.hl7.fhir.r4.model.Measure)resource; - if (!measure.hasLibrary() || measure.getLibrary().size() != 1) { - throw new IllegalArgumentException("Measure is expected to have one and only one library"); - } - return getTail(measure.getLibrary().get(0).getValue()); - } - default: - throw new IllegalArgumentException("Unsupported fhir version: " + fhirContext.getVersion().getVersion().getFhirVersionString()); - } - } - - public static Map getActivityDefinitionResources(String planDefinitionPath, FhirContext fhirContext, Boolean includeVersion) { - Map activityDefinitions = new HashMap(); - IBaseResource planDefinition = IOUtils.readResource(planDefinitionPath, fhirContext, true); - Object actionChild = resolveProperty(planDefinition, "action", fhirContext); - - if (actionChild != null) { - if (actionChild instanceof Iterable) - { + if (actionChild != null) { + if (actionChild instanceof Iterable) + { for (Object action : (Iterable)actionChild) { - Object definitionChild = resolveProperty(action, "definition", fhirContext); - if (definitionChild != null) { - Object referenceChild = resolveProperty(definitionChild, "reference", fhirContext); - - String activityDefinitionId = null; - // NOTE: A bit of a hack. This whole method probably needs to be refactored to consider different FHIR - // versions and the respective ActivityDefinition differences between them. - if (fhirContext.getVersion().getVersion().isEquivalentTo(FhirVersionEnum.R4)) { - activityDefinitionId = CanonicalUtils.getId((CanonicalType)referenceChild); - } else { - String activityDefinitionReference = referenceChild.toString(); - activityDefinitionId = activityDefinitionReference.replaceAll("ActivityDefinition/", "activitydefinition-").replaceAll("_", "-"); - } - - for (String path : IOUtils.getActivityDefinitionPaths(fhirContext)) { - if (path.contains(activityDefinitionId)) { - activityDefinitions.put(path, IOUtils.readResource(path, fhirContext)); - break; + Object definitionChild = resolveProperty(action, "definition", fhirContext); + if (definitionChild != null) { + Object referenceChild = resolveProperty(definitionChild, "reference", fhirContext); + + String activityDefinitionId = null; + // NOTE: A bit of a hack. This whole method probably needs to be refactored to consider different FHIR + // versions and the respective ActivityDefinition differences between them. + if (fhirContext.getVersion().getVersion().isEquivalentTo(FhirVersionEnum.R4)) { + activityDefinitionId = CanonicalUtils.getId((CanonicalType)referenceChild); + } else { + String activityDefinitionReference = referenceChild.toString(); + activityDefinitionId = activityDefinitionReference.replace("ActivityDefinition/", "activitydefinition-").replace("_", "-"); + } + + for (String path : IOUtils.getActivityDefinitionPaths(fhirContext)) { + if (path.contains(activityDefinitionId)) { + activityDefinitions.put(path, IOUtils.readResource(path, fhirContext)); + break; + } } - } - } + } } - } - else { + } + else { Object definitionChild = resolveProperty(actionChild, "definition", fhirContext); if (definitionChild != null) { - Object referenceChild = resolveProperty(definitionChild, "reference", fhirContext); + Object referenceChild = resolveProperty(definitionChild, "reference", fhirContext); - String activityDefinitionReference = (String)referenceChild; + String activityDefinitionReference = (String)referenceChild; - for (String path : IOUtils.getActivityDefinitionPaths(fhirContext)) { - if (path.contains(activityDefinitionReference)) { - activityDefinitions.put(path, IOUtils.readResource(path, fhirContext)); - } - } + for (String path : IOUtils.getActivityDefinitionPaths(fhirContext)) { + if (path.contains(activityDefinitionReference)) { + activityDefinitions.put(path, IOUtils.readResource(path, fhirContext)); + } + } } - } - } - return activityDefinitions; - } + } + } + return activityDefinitions; + } - public static Object resolveProperty(Object target, String path, FhirContext fhirContext) { + public static Object resolveProperty(Object target, String path, FhirContext fhirContext) { if (target == null) { - return null; + return null; } IBase base = (IBase) target; if (base instanceof IPrimitiveType) { - return path.equals("value") ? ((IPrimitiveType) target).getValue() : target; + return path.equals("value") ? ((IPrimitiveType) target).getValue() : target; } BaseRuntimeElementCompositeDefinition definition = resolveRuntimeDefinition(base, fhirContext); BaseRuntimeChildDefinition child = definition.getChildByName(path); if (child == null) { - child = resolveChoiceProperty(definition, path); + child = resolveChoiceProperty(definition, path); } List values = child.getAccessor().getValues(base); if (values == null || values.isEmpty()) { - return null; + return null; } if (child instanceof RuntimeChildChoiceDefinition && !child.getElementName().equalsIgnoreCase(path)) { - if (!values.get(0).getClass().getSimpleName().equalsIgnoreCase(child.getChildByName(path).getImplementingClass().getSimpleName())) - { - return null; - } + if (!values.get(0).getClass().getSimpleName().equalsIgnoreCase(child.getChildByName(path).getImplementingClass().getSimpleName())) + { + return null; + } } //Hack to get DecimalType to work if (child.getMax() == 1 && values.get(0) instanceof org.hl7.fhir.dstu3.model.DecimalType) { - return resolveProperty(values.get(0), "value", fhirContext); + return resolveProperty(values.get(0), "value", fhirContext); } if (child.getMax() == 1 && values.get(0) instanceof org.hl7.fhir.r4.model.DecimalType) { - return resolveProperty(values.get(0), "value", fhirContext); + return resolveProperty(values.get(0), "value", fhirContext); } return child.getMax() < 1 ? values : values.get(0); - } + } - public static BaseRuntimeElementCompositeDefinition resolveRuntimeDefinition(IBase base, FhirContext fhirContext) { + public static BaseRuntimeElementCompositeDefinition resolveRuntimeDefinition(IBase base, FhirContext fhirContext) { if (base instanceof IBaseResource) { - return fhirContext.getResourceDefinition((IBaseResource) base); + return fhirContext.getResourceDefinition((IBaseResource) base); } else if (base instanceof IBaseBackboneElement || base instanceof IBaseElement) { - return (BaseRuntimeElementCompositeDefinition) fhirContext.getElementDefinition(base.getClass()); + return (BaseRuntimeElementCompositeDefinition) fhirContext.getElementDefinition(base.getClass()); } else if (base instanceof ICompositeType) { - return (RuntimeCompositeDatatypeDefinition) fhirContext.getElementDefinition(base.getClass()); + return (RuntimeCompositeDatatypeDefinition) fhirContext.getElementDefinition(base.getClass()); } //should be UnkownType throw new Error(String.format("Unable to resolve the runtime definition for %s", base.getClass().getName())); - } + } - public static BaseRuntimeChildDefinition resolveChoiceProperty(BaseRuntimeElementCompositeDefinition definition, String path) { - for (Object child : definition.getChildren()) { - if (child instanceof RuntimeChildChoiceDefinition) { - RuntimeChildChoiceDefinition choiceDefinition = (RuntimeChildChoiceDefinition) child; + public static BaseRuntimeChildDefinition resolveChoiceProperty(BaseRuntimeElementCompositeDefinition definition, String path) { + for (var child : definition.getChildren()) { + if (child instanceof RuntimeChildChoiceDefinition) { + RuntimeChildChoiceDefinition choiceDefinition = (RuntimeChildChoiceDefinition) child; - if (choiceDefinition.getElementName().startsWith(path)) { - return choiceDefinition; - } - } + if (choiceDefinition.getElementName().startsWith(path)) { + return choiceDefinition; + } + } } //UnkownType throw new Error(String.format("Unable to resolve path %s for %s", path, definition.getName())); - } - - public static RuntimeResourceDefinition getResourceDefinition(FhirContext fhirContext, String ResourceName) { - RuntimeResourceDefinition def = fhirContext.getResourceDefinition(ResourceName); - return def; - } - - public static BaseRuntimeElementDefinition getElementDefinition(FhirContext fhirContext, String ElementName) { - BaseRuntimeElementDefinition def = fhirContext.getElementDefinition(ElementName); - return def; - } - - public static void outputResource(IBaseResource resource, String encoding, FhirContext context, String outputPath) { - try (FileOutputStream writer = new FileOutputStream(outputPath + "/" + resource.getIdElement().getResourceType() + "-" + resource.getIdElement().getIdPart() + "." + encoding)) { - writer.write( - encoding.equals("json") - ? context.newJsonParser().setPrettyPrint(true).encodeResourceToString(resource).getBytes() - : context.newXmlParser().setPrettyPrint(true).encodeResourceToString(resource).getBytes() - ); - writer.flush(); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(e.getMessage()); - } - } - - public static void outputResourceByName(IBaseResource resource, String encoding, FhirContext context, String outputPath, String name) { - try (FileOutputStream writer = new FileOutputStream(outputPath + "/" + name + "." + encoding)) { - writer.write( - encoding.equals("json") - ? context.newJsonParser().setPrettyPrint(true).encodeResourceToString(resource).getBytes() - : context.newXmlParser().setPrettyPrint(true).encodeResourceToString(resource).getBytes() - ); - writer.flush(); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(e.getMessage()); - } - } + } + + public static RuntimeResourceDefinition getResourceDefinition(FhirContext fhirContext, String resourceName) { + return fhirContext.getResourceDefinition(resourceName); + } + + public static BaseRuntimeElementDefinition getElementDefinition(FhirContext fhirContext, String elementName) { + return fhirContext.getElementDefinition(elementName); + } + + public static void outputResource(IBaseResource resource, String encoding, FhirContext context, String outputPath) { + try (FileOutputStream writer = new FileOutputStream(outputPath + "/" + resource.getIdElement().getResourceType() + "-" + resource.getIdElement().getIdPart() + "." + encoding)) { + writer.write( + encoding.equals("json") + ? context.newJsonParser().setPrettyPrint(true).encodeResourceToString(resource).getBytes() + : context.newXmlParser().setPrettyPrint(true).encodeResourceToString(resource).getBytes() + ); + writer.flush(); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e.getMessage()); + } + } + + public static void outputResourceByName(IBaseResource resource, String encoding, FhirContext context, String outputPath, String name) { + try (FileOutputStream writer = new FileOutputStream(outputPath + "/" + name + "." + encoding)) { + writer.write( + encoding.equals("json") + ? context.newJsonParser().setPrettyPrint(true).encodeResourceToString(resource).getBytes() + : context.newXmlParser().setPrettyPrint(true).encodeResourceToString(resource).getBytes() + ); + writer.flush(); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e.getMessage()); + } + } } diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/utilities/constants/CqfmConstants.java b/tooling/src/main/java/org/opencds/cqf/tooling/utilities/constants/CqfmConstants.java new file mode 100644 index 000000000..3d3119cf0 --- /dev/null +++ b/tooling/src/main/java/org/opencds/cqf/tooling/utilities/constants/CqfmConstants.java @@ -0,0 +1,19 @@ +package org.opencds.cqf.tooling.utilities.constants; + +// constants defined in the Quality Measures IG: http://hl7.org/fhir/us/cqfmeasures +public class CqfmConstants { + + private CqfmConstants() { + + } + + // Extensions + public static final String PARAMETERS_EXT_URL = "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-parameter"; + public static final String DATA_REQUIREMENT_EXT_URL = "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-dataRequirement"; + public static final String DIRECT_REF_CODE_EXT_URL = "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-directReferenceCode"; + public static final String LOGIC_DEFINITION_EXT_URL = "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-logicDefinition"; + public static final String EFFECTIVE_DATA_REQS_EXT_URL = "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-effectiveDataRequirements"; + + // Profiles + public static final String COMPUTABLE_MEASURE_PROFILE_URL = "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/computable-measure-cqfm"; +} diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/utilities/converters/ResourceAndTypeConverter.java b/tooling/src/main/java/org/opencds/cqf/tooling/utilities/converters/ResourceAndTypeConverter.java new file mode 100644 index 000000000..9bb6f355d --- /dev/null +++ b/tooling/src/main/java/org/opencds/cqf/tooling/utilities/converters/ResourceAndTypeConverter.java @@ -0,0 +1,111 @@ +package org.opencds.cqf.tooling.utilities.converters; + +import ca.uhn.fhir.context.FhirContext; +import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_30_50; +import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50; +import org.hl7.fhir.convertors.conv30_50.VersionConvertor_30_50; +import org.hl7.fhir.convertors.conv40_50.VersionConvertor_40_50; +import org.hl7.fhir.instance.model.api.IBaseDatatype; +import org.hl7.fhir.instance.model.api.IBaseResource; + +public class ResourceAndTypeConverter { + private static final VersionConvertor_30_50 stu3ToR5Converter = new VersionConvertor_30_50(new BaseAdvisor_30_50()); + private static final VersionConvertor_40_50 r4ToR5Converter = new VersionConvertor_40_50(new BaseAdvisor_40_50()); + + private ResourceAndTypeConverter() { + + } + + public static IBaseDatatype convertType(FhirContext fhirContext, IBaseDatatype type) { + switch (fhirContext.getVersion().getVersion()) { + case R5: + if (type instanceof org.hl7.fhir.dstu3.model.Type) { + return stu3ToR5Type((org.hl7.fhir.dstu3.model.Type) type); + } + else if (type instanceof org.hl7.fhir.r4.model.Type) { + return r4ToR5Type((org.hl7.fhir.r4.model.Type) type); + } + else { + throw new UnsupportedOperationException( + "Conversion to R5 type not supported for " + type.getClass().toString()); + } + case R4: + if (type instanceof org.hl7.fhir.r5.model.DataType) { + return r5ToR4Type((org.hl7.fhir.r5.model.DataType) type); + } + else { + throw new UnsupportedOperationException( + "Conversion to R4 type not supported for " + type.getClass().toString()); + } + case DSTU3: + if (type instanceof org.hl7.fhir.r5.model.DataType) { + return r5ToStu3Type((org.hl7.fhir.r5.model.DataType) type); + } + else { + throw new UnsupportedOperationException( + "Conversion to DSTU3 type not supported for " + type.getClass().toString()); + } + default: + throw new UnsupportedOperationException( + "Conversion not supported for types using version " + + fhirContext.getVersion().getVersion().getFhirVersionString()); + } + } + + public static org.hl7.fhir.r5.model.Resource convertToR5Resource(FhirContext fhirContext, IBaseResource resource) { + switch (fhirContext.getVersion().getVersion()) { + case R5: return (org.hl7.fhir.r5.model.Resource) resource; + case R4: return r4ToR5Resource(resource); + case DSTU3: return stu3ToR5Resource(resource); + default: + throw new UnsupportedOperationException( + "Conversion to R5 not supported for resources using version " + + fhirContext.getVersion().getVersion().getFhirVersionString()); + } + } + + public static IBaseResource convertFromR5Resource(FhirContext fhirContext, org.hl7.fhir.r5.model.Resource resource) { + switch (fhirContext.getVersion().getVersion()) { + case R5: return resource; + case R4: return r5ToR4Resource(resource); + case DSTU3: return r5ToStu3Resource(resource); + default: + throw new UnsupportedOperationException( + "Conversion from R5 not supported for resources using version " + + fhirContext.getVersion().getVersion().getFhirVersionString()); + } + } + + public static org.hl7.fhir.r5.model.Resource stu3ToR5Resource(IBaseResource resourceToConvert) { + return stu3ToR5Converter.convertResource((org.hl7.fhir.dstu3.model.Resource) resourceToConvert); + } + + public static org.hl7.fhir.dstu3.model.Resource r5ToStu3Resource(IBaseResource resourceToConvert) { + return stu3ToR5Converter.convertResource((org.hl7.fhir.r5.model.Resource) resourceToConvert); + } + + public static org.hl7.fhir.r5.model.DataType stu3ToR5Type(org.hl7.fhir.dstu3.model.Type typeToConvert) { + return stu3ToR5Converter.convertType(typeToConvert); + } + + public static org.hl7.fhir.dstu3.model.Type r5ToStu3Type(org.hl7.fhir.r5.model.DataType typeToConvert) { + return stu3ToR5Converter.convertType(typeToConvert); + } + + public static org.hl7.fhir.r5.model.Resource r4ToR5Resource(IBaseResource resourceToConvert) { + return r4ToR5Converter.convertResource((org.hl7.fhir.r4.model.Resource) resourceToConvert); + } + + public static org.hl7.fhir.r4.model.Resource r5ToR4Resource(IBaseResource resourceToConvert) { + return r4ToR5Converter.convertResource((org.hl7.fhir.r5.model.Resource) resourceToConvert); + } + + public static org.hl7.fhir.r5.model.DataType r4ToR5Type(org.hl7.fhir.r4.model.Type typeToConvert) { + return r4ToR5Converter.convertType(typeToConvert); + } + + public static org.hl7.fhir.r4.model.Type r5ToR4Type(org.hl7.fhir.r5.model.DataType typeToConvert) { + return r4ToR5Converter.convertType(typeToConvert); + } + +} diff --git a/tooling/src/test/java/org/opencds/cqf/tooling/library/r4/R4LibraryProcessorTest.java b/tooling/src/test/java/org/opencds/cqf/tooling/library/r4/R4LibraryProcessorTest.java index fa001df14..36cdd745d 100644 --- a/tooling/src/test/java/org/opencds/cqf/tooling/library/r4/R4LibraryProcessorTest.java +++ b/tooling/src/test/java/org/opencds/cqf/tooling/library/r4/R4LibraryProcessorTest.java @@ -1,9 +1,11 @@ package org.opencds.cqf.tooling.library.r4; +import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import java.io.File; import java.util.ArrayList; +import java.util.Objects; import org.apache.commons.io.FileUtils; import org.opencds.cqf.tooling.RefreshTest; @@ -16,14 +18,14 @@ import ca.uhn.fhir.context.FhirVersionEnum; public class R4LibraryProcessorTest extends LibraryProcessorTest { - private String resourceDirectory = "r4"; + private final String resourceDirectory = "r4"; public R4LibraryProcessorTest() { super(new R4LibraryProcessor(), FhirContext.forCached(FhirVersionEnum.R4), "R4LibraryProcessorTest"); } @BeforeMethod public void setUp() throws Exception { - IOUtils.resourceDirectories = new ArrayList(); + IOUtils.resourceDirectories = new ArrayList<>(); IOUtils.clearDevicePaths(); File dir = new File("target" + separator + "refreshLibraries" + separator + "r4"); if (dir.exists()) { @@ -32,7 +34,7 @@ public void setUp() throws Exception { } @Test - private void testRefreshOverwriteLibraries() throws Exception { + void testRefreshOverwriteLibraries() throws Exception { String targetDirectory = "target" + separator + "refreshLibraries" + separator + this.resourceDirectory; copyResourcesToTargetDir(targetDirectory, this.resourceDirectory); @@ -48,14 +50,14 @@ private void testRefreshOverwriteLibraries() throws Exception { } @Test - private void testRefreshOutputDirectory() throws Exception { + void testRefreshOutputDirectory() { // create a output directory under target directory File targetDirectory = new File("target" + separator + "refreshLibraries" + separator + resourceDirectory); if (!targetDirectory.exists()) { targetDirectory.mkdirs(); } - String resourceDirPath = RefreshTest.class.getResource(resourceDirectory).getPath(); - assertTrue(targetDirectory.listFiles().length == 0); + String resourceDirPath = Objects.requireNonNull(RefreshTest.class.getResource(resourceDirectory)).getPath(); + assertEquals(Objects.requireNonNull(targetDirectory.listFiles()).length, 0); String libraryPath = separator + "input" + separator + "resources" + separator + "library" + separator + "library-EXM124_FHIR4-8.2.000.json"; runRefresh( @@ -66,6 +68,6 @@ private void testRefreshOutputDirectory() throws Exception { false ); - assertTrue(targetDirectory.listFiles().length > 0); + assertTrue(Objects.requireNonNull(targetDirectory.listFiles()).length > 0); } } diff --git a/tooling/src/test/java/org/opencds/cqf/tooling/npm/NpmPackageManagerTests.java b/tooling/src/test/java/org/opencds/cqf/tooling/npm/NpmPackageManagerTests.java deleted file mode 100644 index 284f616aa..000000000 --- a/tooling/src/test/java/org/opencds/cqf/tooling/npm/NpmPackageManagerTests.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.opencds.cqf.tooling.npm; - -import static org.testng.Assert.assertTrue; - -import java.io.IOException; -import java.io.InputStream; - -import org.hl7.elm.r1.VersionedIdentifier; -import org.hl7.fhir.r5.context.IWorkerContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.Ignore; -import org.testng.annotations.Test; - -public class NpmPackageManagerTests implements IWorkerContext.ILoggingService { - - private Logger logger = LoggerFactory.getLogger(NpmPackageManagerTests.class); - /* - NOTE: This test depends on the dev package cache for the [sample-ig](https://github.com/FHIR/sample-ig) - Running the IG publisher on a clone of this IG locally will create and cache the package - - TODO: Update this so that it can download and cache packages by itself - */ - @Ignore("Currently requires running the IG Publisher first to cache npm packages.") - @Test - public void TestSampleIG() throws IOException { - NpmPackageManager pm = NpmPackageManager.fromStream(NpmPackageManagerTests.class.getResourceAsStream("myig.xml"), "4.0.1"); - assertTrue(pm.getNpmList().size() >= 1); - } - - /* - NOTE: This test depends on the dev package cache for the [sample-content-ig](https://github.com/cqframework/sample-content-ig) - Running the IG publisher on a clone of this IG locally will create and cache the package - - NOTE: Temporarily @Ignore because it's causing the CI build to fail. - TODO: Update this so that it can download and cache packages by itself - */ - @Ignore("Currently requires running the IG Publisher first to cache npm packages.") - @Test - public void TestSampleContentIG() throws IOException { - NpmPackageManager pm = NpmPackageManager.fromStream(NpmPackageManagerTests.class.getResourceAsStream("mycontentig.xml"), "4.0.1"); - assertTrue(pm.getNpmList().size() >= 1); - } - - /* - NOTE: This test depends on the dev package cache for the [opioid-mme-r4](https://github.com/cqframework/opioid-mme-r4) - Running the IG publisher on a clone of this IG locally will create and cache the package - - NOTE: Also disabled due to causing the CI build to fail. - */ - @Ignore - @Test - public void TestOpioidMMEIG() throws IOException { - NpmPackageManager pm = NpmPackageManager.fromStream(NpmPackageManagerTests.class.getResourceAsStream("opioid-mme-r4.xml"), "4.0.1"); - assertTrue(pm.getNpmList().size() >= 1); - } - - /* - NOTE: This test depends on the dev package cache for the [sample-content-ig](https://github.com/cqframework/sample-content-ig) - Running the IG publisher on a clone of this IG locally will create and cache the package - - NOTE: Temporarily @Ignore because it's causing the CI build to fail. - */ - @Ignore - @Test - public void TestLibrarySourceProvider() throws IOException { - NpmPackageManager pm = NpmPackageManager.fromStream(NpmPackageManagerTests.class.getResourceAsStream("mycontentig.xml"), "4.0.1"); - assertTrue(pm.getNpmList().size() >= 1); - - LibraryLoader reader = new LibraryLoader("4.0.1"); - NpmLibrarySourceProvider sp = new NpmLibrarySourceProvider(pm.getNpmList(), reader, this); - InputStream is = sp.getLibrarySource(new VersionedIdentifier().withSystem("http://somewhere.org/fhir/uv/myig").withId("example")); - assertTrue(is != null); - is = sp.getLibrarySource(new VersionedIdentifier().withSystem("http://somewhere.org/fhir/uv/myig").withId("example").withVersion("0.2.0")); - assertTrue(is != null); - } - - @Override - public void logMessage(String msg) { - logger.info(msg); - } - - @Override - public void logDebugMessage(IWorkerContext.ILoggingService.LogCategory category, String msg) { - logMessage(msg); - } -} diff --git a/tooling/src/test/java/org/opencds/cqf/tooling/operation/RefreshIGOperationTest.java b/tooling/src/test/java/org/opencds/cqf/tooling/operation/RefreshIGOperationTest.java index 0a1c32c2e..027ee9cd3 100644 --- a/tooling/src/test/java/org/opencds/cqf/tooling/operation/RefreshIGOperationTest.java +++ b/tooling/src/test/java/org/opencds/cqf/tooling/operation/RefreshIGOperationTest.java @@ -2,13 +2,13 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertSame; import static org.testng.Assert.assertTrue; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; @@ -24,11 +24,14 @@ import java.util.Map; import org.apache.commons.io.FileUtils; +import org.cqframework.fhir.utilities.exception.IGInitializationException; +import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.utilities.IniFile; import org.opencds.cqf.tooling.RefreshTest; import org.opencds.cqf.tooling.library.LibraryProcessor; import org.opencds.cqf.tooling.measure.MeasureProcessor; +import org.opencds.cqf.tooling.operation.ig.NewRefreshIGOperation; import org.opencds.cqf.tooling.parameter.RefreshIGParameters; import org.opencds.cqf.tooling.processor.CDSHooksProcessor; import org.opencds.cqf.tooling.processor.IGBundleProcessor; @@ -51,9 +54,6 @@ public class RefreshIGOperationTest extends RefreshTest { public RefreshIGOperationTest() { super(FhirContext.forCached(FhirVersionEnum.R4)); } - - private static final String EXCEPTIONS_OCCURRED_LOADING_IG_FILE = "Exceptions occurred loading IG file"; - private static final String EXCEPTIONS_OCCURRED_INITIALIZING_REFRESH_FROM_INI_FILE = "Exceptions occurred initializing refresh from ini file"; private final String ID = "id"; private final String ENTRY = "entry"; private final String RESOURCE = "resource"; @@ -64,31 +64,29 @@ public RefreshIGOperationTest() { private final String INI_LOC = "target" + separator + "refreshIG" + separator + "ig.ini"; - - // Store the original standard out before changing it. - private final PrintStream originalStdOut = System.out; - private ByteArrayOutputStream console = new ByteArrayOutputStream(); - - @BeforeMethod - public void setUp() throws Exception { - IOUtils.resourceDirectories = new ArrayList(); - IOUtils.clearDevicePaths(); - System.setOut(new PrintStream(this.console)); - File dir = new File("target" + separator + "refreshIG"); - if (dir.exists()) { - FileUtils.deleteDirectory(dir); - } + // Store the original standard out before changing it. + private final PrintStream originalStdOut = System.out; + private ByteArrayOutputStream console = new ByteArrayOutputStream(); + + @BeforeMethod + public void setUp() throws Exception { + IOUtils.resourceDirectories = new ArrayList<>(); + IOUtils.clearDevicePaths(); + System.setOut(new PrintStream(this.console)); + File dir = new File("target" + separator + "refreshIG"); + if (dir.exists()) { + FileUtils.deleteDirectory(dir); + } deleteTempINI(); - } + } /** - * This test breaks down refreshIG's process and can verify multiple bundles + * This test breaks down refreshIG operation's process and can verify multiple bundles */ @SuppressWarnings("unchecked") @Test - //TODO: Fix separately, this is blocking a bunch of other higher priority things - public void testBundledFiles() throws IOException { + void testBundledFiles() throws IOException { copyResourcesToTargetDir("target" + separator + "refreshIG", "testfiles/refreshIG"); // build ini object File iniFile = new File(INI_LOC); @@ -97,12 +95,12 @@ public void testBundledFiles() throws IOException { String bundledFilesLocation = iniFile.getParent() + separator + "bundles" + separator + "measure" + separator; - String args[] = { "-RefreshIG", "-ini=" + INI_LOC, "-t", "-d", "-p", "-e=json", "-ts=false" }; + String[] args = { "-RefreshIG", "-ini=" + INI_LOC, "-t", "-d", "-p", "-e=json", "-ts=false" }; // execute refresh using ARGS - new RefreshIGOperation().execute(args); + new RefreshIGOperation().execute(args); - // determine fhireContext for measure lookup + // determine fhirContext for measure lookup FhirContext fhirContext = IGProcessor.getIgFhirContext(getFhirVersion(ini)); // get list of measures resulting from execution @@ -114,7 +112,7 @@ public void testBundledFiles() throws IOException { // location of single bundled file: final String bundledFileResult = bundledFilesLocation + measureName + separator + measureName + "-bundle.json"; - // multiple individual files in sub directory to loop through: + // multiple individual files in subdirectory to loop through: final Path dir = Paths .get(bundledFilesLocation + separator + measureName + separator + measureName + "-files"); @@ -173,89 +171,89 @@ public void testBundledFiles() throws IOException { // compare mappings of to ensure all bundled correctly: assertTrue(mapsAreEqual(resourceTypeMap, bundledJsonResourceTypes)); - } } - //@Test(expectedExceptions = IllegalArgumentException.class) - //TODO: Fix separately, this is blocking a bunch of other higher priority things - public void testNullArgs() { + @Test(expectedExceptions = IllegalArgumentException.class) + void testNullArgs() { new RefreshIGOperation().execute(null); } - //@Test - //TODO: Fix separately, this is blocking a bunch of other higher priority things - public void testBlankINILoc() { - String args[] = { "-RefreshIG", "-ini=", "-t", "-d", "-p" }; + @Test + void testBlankINILoc() { + String[] args = { "-RefreshIG", "-ini=", "-t", "-d", "-p" }; try { new RefreshIGOperation().execute(args); } catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), IGProcessor.IG_VERSION_REQUIRED); - assertTrue(this.console.toString().indexOf("fhir-version was not specified in the ini file.") != -1); + assertEquals(e.getMessage(), IGProcessor.INVALID_INI); } } - - //@Test - //TODO: Fix separately, this is blocking a bunch of other higher priority things - public void testInvalidIgVersion() { - Map igProperties = new HashMap(); + @Test + void testInvalidIgVersion() { + Map igProperties = new HashMap<>(); igProperties.put("ig", "nonsense"); igProperties.put("template", "nonsense"); igProperties.put("usage-stats-opt-out", "nonsense"); igProperties.put("fhir-version", "nonsense"); File iniFile = this.createTempINI(igProperties); + assert iniFile != null; - String args[] = { "-RefreshIG", "-ini=" + iniFile.getAbsolutePath(), "-t", "-d", "-p" }; - - if (iniFile != null) { - try { - new RefreshIGOperation().execute(args); - } catch (Exception e) { - assertTrue(e.getClass() == IllegalArgumentException.class); - assertTrue(this.console.toString().indexOf(EXCEPTIONS_OCCURRED_INITIALIZING_REFRESH_FROM_INI_FILE) != -1); - assertTrue(this.console.toString().indexOf("Unknown Version 'nonsense'") != -1); + String[] args = { "-RefreshIG", "-ini=" + iniFile.getAbsolutePath(), "-t", "-d", "-p" }; - assertEquals(e.getMessage(), IGProcessor.IG_VERSION_REQUIRED); - } - deleteTempINI(); + try { + new RefreshIGOperation().execute(args); + } catch (Exception e) { + assertSame(e.getClass(), FHIRException.class); + assertTrue(e.getMessage().contains("Unknown Version 'nonsense'")); } + deleteTempINI(); } - //@Test - //TODO: Fix separately, this is blocking a bunch of other higher priority things - public void testInvalidIgInput() { - Map igProperties = new HashMap(); + @Test + void testInvalidIgInput() { + Map igProperties = new HashMap<>(); igProperties.put("ig", "nonsense"); igProperties.put("template", "nonsense"); igProperties.put("usage-stats-opt-out", "nonsense"); igProperties.put("fhir-version", "4.0.1"); File iniFile = this.createTempINI(igProperties); + assert iniFile != null; - String args[] = { "-RefreshIG", "-ini=" + iniFile.getAbsolutePath(), "-t", "-d", "-p" }; + String[] args = { "-RefreshIG", "-ini=" + iniFile.getAbsolutePath(), "-t", "-d", "-p" }; - if (iniFile != null) { - try { - new RefreshIGOperation().execute(args); - } catch (Exception e) { - assertTrue(e.getClass() == IllegalArgumentException.class); - assertEquals(e.getMessage(), IGProcessor.IG_VERSION_REQUIRED); - - assertTrue(this.console.toString().indexOf(EXCEPTIONS_OCCURRED_LOADING_IG_FILE) != -1); - assertTrue(this.console.toString().indexOf(EXCEPTIONS_OCCURRED_INITIALIZING_REFRESH_FROM_INI_FILE) != -1); - } - deleteTempINI(); + try { + new RefreshIGOperation().execute(args); + } catch (Exception e) { + assertSame(e.getClass(), IGInitializationException.class); + assertTrue(e.getMessage().contains("Exceptions occurred loading IG path")); } + deleteTempINI(); + } + + @Test + void testOpioidIGRefresh() { + String[] args = { + "-RefreshIG", "-rd=/Users/christopherschuler/Documents/workspace/cqframework/igs/opioid-cds-r4", + "-ini=/Users/christopherschuler/Documents/workspace/cqframework/igs/opioid-cds-r4/ig.ini", + "-rp=/Users/christopherschuler/Documents/workspace/cqframework/igs/opioid-cds-r4/input/resources", + "-t", "-d", "-p", "-ss=false" }; + new NewRefreshIGOperation().execute(args); } + @Test + void localVSExpandTest() { + String[] args = { "-LocalVSExpand" }; + new LocalValueSetExpand().execute(args); + } - //@Test + @Test //TODO: Fix separately, this is blocking a bunch of other higher priority things - public void testParamsMissingINI() { - Map igProperties = new HashMap(); + void testParamsMissingINI() { + Map igProperties = new HashMap<>(); igProperties.put("ig", "nonsense"); igProperties.put("template", "nonsense"); igProperties.put("usage-stats-opt-out", "nonsense"); @@ -263,45 +261,44 @@ public void testParamsMissingINI() { File iniFile = this.createTempINI(igProperties); - String args[] = { "-RefreshIG", "-ini=" + iniFile.getAbsolutePath(), "-t", "-d", "-p" }; - - RefreshIGParameters params = null; - try { - params = new RefreshIGArgumentProcessor().parseAndConvert(args); - } - catch (Exception e) { - System.err.println(e.getMessage()); - System.exit(1); - } - MeasureProcessor measureProcessor = new MeasureProcessor(); - LibraryProcessor libraryProcessor = new LibraryProcessor(); - CDSHooksProcessor cdsHooksProcessor = new CDSHooksProcessor(); - PlanDefinitionProcessor planDefinitionProcessor = new PlanDefinitionProcessor(libraryProcessor, cdsHooksProcessor); - QuestionnaireProcessor questionnaireProcessor = new QuestionnaireProcessor(libraryProcessor); - IGBundleProcessor igBundleProcessor = new IGBundleProcessor(measureProcessor, planDefinitionProcessor, questionnaireProcessor); - IGProcessor processor = new IGProcessor(igBundleProcessor, libraryProcessor, measureProcessor); + String[] args = { "-RefreshIG", "-ini=" + iniFile.getAbsolutePath(), "-t", "-d", "-p" }; - //override ini to be null - params.ini = null; + RefreshIGParameters params = null; + try { + params = new RefreshIGArgumentProcessor().parseAndConvert(args); + } + catch (Exception e) { + System.err.println(e.getMessage()); + System.exit(1); + } + MeasureProcessor measureProcessor = new MeasureProcessor(); + LibraryProcessor libraryProcessor = new LibraryProcessor(); + CDSHooksProcessor cdsHooksProcessor = new CDSHooksProcessor(); + PlanDefinitionProcessor planDefinitionProcessor = new PlanDefinitionProcessor(libraryProcessor, cdsHooksProcessor); + QuestionnaireProcessor questionnaireProcessor = new QuestionnaireProcessor(libraryProcessor); + IGBundleProcessor igBundleProcessor = new IGBundleProcessor(measureProcessor, planDefinitionProcessor, questionnaireProcessor); + IGProcessor processor = new IGProcessor(igBundleProcessor, libraryProcessor, measureProcessor); + //override ini to be null + params.ini = null; - try { + try { processor.publishIG(params); } catch (Exception e) { - assertEquals(e.getClass(), NullPointerException.class); + assertEquals(e.getClass(), IllegalArgumentException.class); + assertTrue(e.getMessage().contains("Either the ini argument or both igPath and rootDir must be provided")); } - deleteTempINI(); + deleteTempINI(); } - - @AfterMethod - public void afterTest() { + @AfterMethod + public void afterTest() { deleteTempINI(); - System.setOut(this.originalStdOut); - System.out.println(this.console.toString()); - this.console = new ByteArrayOutputStream(); - } + System.setOut(this.originalStdOut); + System.out.println(this.console.toString()); + this.console = new ByteArrayOutputStream(); + } private File createTempINI(Map properties) { @@ -352,7 +349,7 @@ private boolean deleteTempINI() { map = gson.fromJson(reader, Map.class); reader.close(); } catch (Exception ex) { - // swallow exception if directory doesnt' exist + // swallow exception if directory doesn't exist // ex.printStackTrace(); } return map; @@ -372,7 +369,7 @@ private boolean mapsAreEqual(Map map1, Map map2) private String getFhirVersion(IniFile ini) { String specifiedFhirVersion = ini.getStringProperty("IG", "fhir-version"); - if (specifiedFhirVersion == null || specifiedFhirVersion == "") { + if (specifiedFhirVersion == null || "".equals(specifiedFhirVersion)) { // TODO: Should point to global constant: specifiedFhirVersion = "4.0.1"; @@ -380,17 +377,13 @@ private String getFhirVersion(IniFile ini) { return specifiedFhirVersion; } - - - - /** * Quick method to delete all valuesets not belonging to CQL and identify * anything missing. * - * @param args + * @param args the operation parameters */ - public static void main(String args[]) { + public static void main(String[] args) { // switch this to true to clean up excess valueset files. boolean deleteExcess = false; @@ -411,7 +404,7 @@ public static void main(String args[]) { if (line.startsWith("valueset")) { valueSetsFound = true; - String vs = line.substring(line.lastIndexOf("/") + 1, line.length()).replace("'", "") + String vs = line.substring(line.lastIndexOf("/") + 1).replace("'", "") .trim(); listOfValueSets.add(vs); } else { @@ -422,8 +415,6 @@ public static void main(String args[]) { } } - } catch (FileNotFoundException e) { - e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } @@ -448,7 +439,7 @@ public static void main(String args[]) { System.out.println("Valueset found not belonging to any cql: " + path); - if (deleteExcess == true) { + if (deleteExcess) { try { Files.delete(path); System.out.println("Deleted " + path); @@ -473,6 +464,5 @@ public static void main(String args[]) { } } } - } } diff --git a/tooling/src/test/java/org/opencds/cqf/tooling/processor/IGProcessorTest.java b/tooling/src/test/java/org/opencds/cqf/tooling/processor/IGProcessorTest.java index 85692300a..2af2b4d2a 100644 --- a/tooling/src/test/java/org/opencds/cqf/tooling/processor/IGProcessorTest.java +++ b/tooling/src/test/java/org/opencds/cqf/tooling/processor/IGProcessorTest.java @@ -23,6 +23,7 @@ import org.opencds.cqf.tooling.RefreshTest; import org.opencds.cqf.tooling.library.LibraryProcessor; import org.opencds.cqf.tooling.measure.MeasureProcessor; +import org.opencds.cqf.tooling.operation.RefreshIGOperation; import org.opencds.cqf.tooling.parameter.RefreshIGParameters; import org.opencds.cqf.tooling.questionnaire.QuestionnaireProcessor; import org.opencds.cqf.tooling.utilities.IOUtils; @@ -39,9 +40,9 @@ import static org.testng.Assert.assertTrue; public class IGProcessorTest extends RefreshTest { - - private IGProcessor processor; - private ByteArrayOutputStream console = new ByteArrayOutputStream(); + + private final IGProcessor processor; + private final ByteArrayOutputStream console = new ByteArrayOutputStream(); private final String ID = "id"; private final String ENTRY = "entry"; @@ -53,51 +54,59 @@ public class IGProcessorTest extends RefreshTest { private final String INI_LOC = "target" + separator + "refreshIG" + separator + "ig.ini"; - public IGProcessorTest() { - super(FhirContext.forCached(FhirVersionEnum.R4), "IGProcessorTest"); - LibraryProcessor libraryProcessor = new LibraryProcessor(); - MeasureProcessor measureProcessor = new MeasureProcessor(); - CDSHooksProcessor cdsHooksProcessor = new CDSHooksProcessor(); - PlanDefinitionProcessor planDefinitionProcessor = new PlanDefinitionProcessor(libraryProcessor, cdsHooksProcessor); + public IGProcessorTest() { + super(FhirContext.forCached(FhirVersionEnum.R4), "IGProcessorTest"); + LibraryProcessor libraryProcessor = new LibraryProcessor(); + MeasureProcessor measureProcessor = new MeasureProcessor(); + CDSHooksProcessor cdsHooksProcessor = new CDSHooksProcessor(); + PlanDefinitionProcessor planDefinitionProcessor = new PlanDefinitionProcessor(libraryProcessor, cdsHooksProcessor); QuestionnaireProcessor questionnaireProcessor = new QuestionnaireProcessor(libraryProcessor); - IGBundleProcessor igBundleProcessor = new IGBundleProcessor(measureProcessor, planDefinitionProcessor, questionnaireProcessor); - processor = new IGProcessor(igBundleProcessor, libraryProcessor, measureProcessor); - } - - @BeforeMethod - public void setUp() throws Exception { - IOUtils.resourceDirectories = new ArrayList(); - IOUtils.clearDevicePaths(); - System.setOut(new PrintStream(this.console)); - File dir = new File("target" + separator + "refreshIG"); - if (dir.exists()) { - FileUtils.deleteDirectory(dir); - } - } - - @Test + IGBundleProcessor igBundleProcessor = new IGBundleProcessor(measureProcessor, planDefinitionProcessor, questionnaireProcessor); + processor = new IGProcessor(igBundleProcessor, libraryProcessor, measureProcessor); + } + + @BeforeMethod + public void setUp() throws Exception { + IOUtils.resourceDirectories = new ArrayList<>(); + IOUtils.clearDevicePaths(); + System.setOut(new PrintStream(this.console)); + File dir = new File("target" + separator + "refreshIG"); + if (dir.exists()) { + FileUtils.deleteDirectory(dir); + } + } + +// @Test +// void refreshOpioidIGTest() { +// RefreshIGOperation refreshOperation = new RefreshIGOperation(); +// String[] args = { "-RefreshIG", "-ini=/Users/christopherschuler/Documents/workspace/cqframework/igs/opioid-cds-r4/ig.ini", "-rp=/Users/christopherschuler/Documents/workspace/cqframework/igs/opioid-cds-r4/input/resources", "-d", "-p", "-t", "-ss=false" }; +// refreshOperation.execute(args); +// String s = "s"; +// } + + @Test @SuppressWarnings("unchecked") - public void testRefreshIG() throws Exception { - String targetDirectory = "target" + separator + "refreshIG"; + public void testRefreshIG() throws Exception { + String targetDirectory = "target" + separator + "refreshIG"; copyResourcesToTargetDir(targetDirectory, "testfiles/refreshIG"); - - File iniFile = new File(INI_LOC); - String iniFileLocation = iniFile.getAbsolutePath(); - IniFile ini = new IniFile(iniFileLocation); - - String bundledFilesLocation = iniFile.getParent() + separator + "bundles" + separator + "measure" + separator; - RefreshIGParameters params = new RefreshIGParameters(); - params.ini = INI_LOC; + + File iniFile = new File(INI_LOC); + String iniFileLocation = iniFile.getAbsolutePath(); + IniFile ini = new IniFile(iniFileLocation); + + String bundledFilesLocation = iniFile.getParent() + separator + "bundles" + separator + "measure" + separator; + RefreshIGParameters params = new RefreshIGParameters(); + params.ini = INI_LOC; params.outputEncoding = IOUtils.Encoding.JSON; - params.resourceDirs = new ArrayList(); + params.resourceDirs = new ArrayList<>(); params.includeELM = false; - params.includeTerminology = true; - params.includeDependencies = true; - params.includePatientScenarios = true; + params.includeTerminology = true; + params.includeDependencies = true; + params.includePatientScenarios = true; params.versioned = false; params.shouldApplySoftwareSystemStamp = true; params.addBundleTimestamp = true; //setting this true to test timestamp added in generated bundle - processor.publishIG(params); + processor.publishIG(params); // determine fhireContext for measure lookup FhirContext fhirContext = IGProcessor.getIgFhirContext(getFhirVersion(ini)); @@ -134,8 +143,8 @@ public void testRefreshIG() throws Exception { String parentResourceType = (String) map.get(RESOURCE_TYPE); // if Library, resource will produce a "Measure" in main bundled file: if (parentResourceType.equalsIgnoreCase(LIB_TYPE)) { - resourceTypeMap.put(MEASURE_TYPE + "_" + (String) map.get(ID), MEASURE_TYPE); - resourceTypeMap.put(LIB_TYPE + "_" + (String) map.get(ID), LIB_TYPE); + resourceTypeMap.put(MEASURE_TYPE + "_" + map.get(ID), MEASURE_TYPE); + resourceTypeMap.put(LIB_TYPE + "_" + map.get(ID), LIB_TYPE); } else if (parentResourceType.equalsIgnoreCase(BUNDLE_TYPE)) { // file is a bundle type, loop through resources in entry list, build up map of // : @@ -144,7 +153,7 @@ public void testRefreshIG() throws Exception { for (Map entry : entryList) { if (entry.containsKey(RESOURCE)) { Map resourceMap = (Map) entry.get(RESOURCE); - resourceTypeMap.put((String) resourceMap.get(RESOURCE_TYPE) + "_" + (String) resourceMap.get(ID), + resourceTypeMap.put(resourceMap.get(RESOURCE_TYPE) + "_" + resourceMap.get(ID), (String) resourceMap.get(RESOURCE_TYPE)); } } @@ -154,7 +163,6 @@ public void testRefreshIG() throws Exception { } } } - } catch (IOException e) { e.printStackTrace(); } @@ -166,14 +174,14 @@ public void testRefreshIG() throws Exception { ArrayList> entryList = (ArrayList>) bundledJson.get(ENTRY); for (Map entry : entryList) { Map resourceMap = (Map) entry.get(RESOURCE); - bundledJsonResourceTypes.put((String) resourceMap.get(RESOURCE_TYPE) + "_" + (String) resourceMap.get(ID), (String) resourceMap.get(RESOURCE_TYPE)); + bundledJsonResourceTypes.put(resourceMap.get(RESOURCE_TYPE) + "_" + resourceMap.get(ID), (String) resourceMap.get(RESOURCE_TYPE)); } // compare mappings of to ensure all bundled correctly: assertTrue(mapsAreEqual(resourceTypeMap, bundledJsonResourceTypes)); } - } + } private void testTimestamp(Map bundledJson) throws ParseException { String timeStamp = (String)bundledJson.get("timestamp"); @@ -211,7 +219,7 @@ private boolean mapsAreEqual(Map map1, Map map2) private String getFhirVersion(IniFile ini) { String specifiedFhirVersion = ini.getStringProperty("IG", "fhir-version"); - if (specifiedFhirVersion == null || specifiedFhirVersion == "") { + if (specifiedFhirVersion == null || "".equals(specifiedFhirVersion)) { // TODO: Should point to global constant: specifiedFhirVersion = "4.0.1";