Skip to content

Commit

Permalink
xversion support #265
Browse files Browse the repository at this point in the history
  • Loading branch information
oliveregger committed Sep 10, 2024
1 parent 7f11889 commit 578de87
Show file tree
Hide file tree
Showing 10 changed files with 301 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,10 @@ public CdaMappingEngineBuilder() {
public CdaMappingEngine getEngineR5() throws FHIRException, IOException, URISyntaxException {
log.info("Initializing CDA Mapping Engine");
log.info(VersionUtil.getPoweredBy());
CdaMappingEngine engine = new CdaMappingEngine(super.fromNothing());
CdaMappingEngine engine = new CdaMappingEngine(this.fromSource("hl7.fhir.r5.core#5.0.0"));
// if the version would have been set before (constructor) the package is loaded
// from the package cache, we don't want this
engine.setVersion("5.0.0");
engine.loadPackage(getClass().getResourceAsStream("/hl7.fhir.r5.core.tgz"));
engine.loadPackage(getClass().getResourceAsStream("/hl7.terminology#5.4.0.tgz"));
engine.loadPackage(getClass().getResourceAsStream("/hl7.fhir.uv.extensions#1.0.0.tgz"));
engine.loadPackage(getClass().getResourceAsStream("/hl7.cda.uv.core#2.0.0-sd-202406-matchbox-patch.tgz"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -41,7 +42,6 @@
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r5.context.ContextUtilities;
import org.hl7.fhir.r5.context.SimpleWorkerContext;
import org.hl7.fhir.r5.context.SimpleWorkerContext.SimpleWorkerContextBuilder;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.elementmodel.Manager;
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
Expand Down Expand Up @@ -72,6 +72,7 @@
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.hl7.fhir.validation.IgLoader;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.services.PassiveExpiringSessionCache;
import org.hl7.fhir.validation.instance.InstanceValidator;

import ch.ahdis.matchbox.engine.cli.VersionUtil;
Expand All @@ -91,10 +92,16 @@ public class MatchboxEngine extends ValidationEngine {

protected static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MatchboxEngine.class);

protected final List<String> suppressedWarnInfoPatterns = new ArrayList<>();
protected List<String> suppressedWarnInfoPatterns = new ArrayList<>();
protected PassiveExpiringSessionCache sessionCache = new PassiveExpiringSessionCache();

public MatchboxEngine(final @NonNull ValidationEngine other) throws FHIRException, IOException {
super(other);
if (other instanceof MatchboxEngine) {
MatchboxEngine otherMatchboxEgine = (MatchboxEngine) other;
this.sessionCache = otherMatchboxEgine.sessionCache;
this.suppressedWarnInfoPatterns = otherMatchboxEgine.suppressedWarnInfoPatterns;
}
// Create a new IgLoader, otherwise the context is desynchronized between the loader and the engine
this.setIgLoader(new IgLoader(this.getPcm(), this.getContext(), this.getVersion(), this.isDebug()));
try {
Expand All @@ -103,6 +110,10 @@ public MatchboxEngine(final @NonNull ValidationEngine other) throws FHIRExceptio
throw new MatchboxEngineCreationException(e);
}
}

public void cacheXVersionEngine(MatchboxEngine engine) {
sessionCache.cacheSession(engine.getVersion().substring(0,3), engine);
}

/**
* Builder class to instantiate a MappingEngine
Expand Down Expand Up @@ -182,16 +193,15 @@ public void setPackageCachePath(final String packageCachePath) {
public MatchboxEngine getEngineR4() throws MatchboxEngineCreationException {
log.info("Initializing Matchbox Engine (FHIR R4 with terminology provided in classpath)");
log.info(VersionUtil.getPoweredBy());
final MatchboxEngine engine;
try { engine = new MatchboxEngine(this.fromNothing()); }
catch (final IOException e) { throw new MatchboxEngineCreationException(e); }
final MatchboxEngine engine ;
try { engine = new MatchboxEngine(this.fromSource("hl7.fhir.r4.core#4.0.1")); }
catch (final Exception e) { throw new MatchboxEngineCreationException(e); }
engine.setVersion(FhirPublication.R4.toCode());
try {
engine.loadPackage(getClass().getResourceAsStream("/hl7.fhir.r4.core.tgz"));
engine.loadPackage(getClass().getResourceAsStream("/hl7.terminology#5.4.0.tgz"));
engine.loadPackage(getClass().getResourceAsStream("/hl7.fhir.uv.extensions.r4#1.0.0.tgz"));
removeStructureMaps(engine);
engine.loadPackage(getClass().getResourceAsStream("/hl7.fhir.uv.xver#0.1.0@bp.tgz"));
engine.loadPackage(getClass().getResourceAsStream("/hl7.fhir.uv.xver#0.1.0@mb.tgz"));
} catch (final IOException e) {
throw new IgLoadException(e);
}
Expand All @@ -214,6 +224,46 @@ public MatchboxEngine getEngineR4() throws MatchboxEngineCreationException {
return engine;
}

/**
* Returns a FHIR R4B engine configured with hl7 terminology
*
* @return
* @throws MatchboxEngineCreationException
*/
public MatchboxEngine getEngineR4B() throws MatchboxEngineCreationException {
log.info("Initializing Matchbox Engine (FHIR R4B with terminology provided in classpath)");
log.info(VersionUtil.getPoweredBy());
final MatchboxEngine engine ;
try { engine = new MatchboxEngine(this.fromSource("hl7.fhir.r4b.core#4.3.0")); }
catch (final Exception e) { throw new MatchboxEngineCreationException(e); }
engine.setVersion(FhirPublication.R4B.toCode());
try {
engine.loadPackage(getClass().getResourceAsStream("/hl7.terminology#5.4.0.tgz"));
engine.loadPackage(getClass().getResourceAsStream("/hl7.fhir.uv.extensions.r4#1.0.0.tgz"));
removeStructureMaps(engine);
engine.loadPackage(getClass().getResourceAsStream("/hl7.fhir.uv.xver#[email protected]"));
} catch (final IOException e) {
throw new IgLoadException(e);
}
if (this.txServer == null) {
engine.getContext().setCanRunWithoutTerminology(true);
engine.getContext().setNoTerminologyServer(true);
} else {
engine.getContext().setCanRunWithoutTerminology(false);
engine.getContext().setNoTerminologyServer(false);
try {
engine.setTerminologyServer(this.txServer, null, FhirPublication.R4, true);
} catch (final Exception e) {
throw new TerminologyServerException(e);
}
}
engine.getContext().setPackageTracker(engine);
engine.setPcm(this.getFilesystemPackageCacheManager());
engine.setPolicyAdvisor(new ValidationPolicyAdvisor(ReferenceValidationPolicy.CHECK_VALID));
engine.setAllowExampleUrls(true);
return engine;
}

/**
* remove old StructureMaps from the context, especially from hl7.fhir.uv.extensions.r4#1.0.0 which are replaced by newer versions
* @param engine
Expand All @@ -234,15 +284,14 @@ public MatchboxEngine getEngineR5() throws MatchboxEngineCreationException {
log.info("Initializing Matchbox Engine (FHIR R5 with terminology provided in classpath)");
log.info(VersionUtil.getPoweredBy());
final MatchboxEngine engine;
try { engine = new MatchboxEngine(this.fromNothing()); }
catch (final IOException e) { throw new MatchboxEngineCreationException(e); }
try { engine = new MatchboxEngine(this.fromSource("hl7.fhir.r5.core#5.0.0")); }
catch (final Exception e) { throw new MatchboxEngineCreationException(e); }
engine.setVersion(FhirPublication.R5.toCode());
try {
engine.loadPackage(getClass().getResourceAsStream("/hl7.fhir.r5.core.tgz"));
engine.loadPackage(getClass().getResourceAsStream("/hl7.terminology#5.4.0.tgz"));
engine.loadPackage(getClass().getResourceAsStream("/hl7.fhir.uv.extensions#1.0.0.tgz"));
removeStructureMaps(engine);
engine.loadPackage(getClass().getResourceAsStream("/hl7.fhir.uv.xver#0.1.0@bp.tgz"));
engine.loadPackage(getClass().getResourceAsStream("/hl7.fhir.uv.xver#0.1.0@mb.tgz"));
} catch (final IOException e) {
throw new IgLoadException(e);
}
Expand Down Expand Up @@ -303,12 +352,21 @@ public MatchboxEngine getEngine() throws MatchboxEngineCreationException {
@Override
public ValidationEngine fromNothing() throws MatchboxEngineCreationException {
try {
return super.fromNothing();
return super.fromNothing();
} catch (final IOException e) {
throw new MatchboxEngineCreationException(e);
}
}

@Override
public ValidationEngine fromSource(String src) throws IOException, URISyntaxException {
try {
return super.fromSource(src);
} catch (final IOException e) {
throw new MatchboxEngineCreationException(e);
}
}

public MatchboxEngineBuilder withVersion(final String version) {
super.withVersion(version);
this.fhirVersion = FhirPublication.fromCode(version);
Expand Down Expand Up @@ -379,8 +437,8 @@ public String transform(String input, boolean inputJson, String mapUri, boolean
StructureMap map = context.fetchResource(StructureMap.class, mapUri);

String fhirVersionTarget = getFhirVersion(getCanonicalFromStructureMap(map, StructureMap.StructureMapModelMode.TARGET));
if (fhirVersionTarget !=null && !fhirVersionTarget.equals(this.getVersion().substring(0, 3))) {
log.info("Loading additional FHIR version for Target into context" + fhirVersionTarget);
if (fhirVersionTarget !=null && (fhirVersionTarget.startsWith("4.0") || fhirVersionTarget.startsWith("4.3") || fhirVersionTarget.startsWith("5.0")) && !fhirVersionTarget.equals(this.getVersion().substring(0, 3))) {
log.info("Loading additional FHIR version for Target into context " + fhirVersionTarget);
context = getContextForFhirVersion(fhirVersionTarget);
}

Expand Down Expand Up @@ -414,8 +472,8 @@ public org.hl7.fhir.r5.elementmodel.Element transform(ByteProvider source, FhirF

StructureMap map = context.fetchResource(StructureMap.class, mapUri);
String fhirVersionSource = getFhirVersion(getCanonicalFromStructureMap(map, StructureMap.StructureMapModelMode.SOURCE));
if (fhirVersionSource !=null && !fhirVersionSource.equals(this.getVersion().substring(0,3))) {
log.info("Loading additional FHIR version for Source into context" + fhirVersionSource);
if (fhirVersionSource !=null && (fhirVersionSource.startsWith("4.0") || fhirVersionSource.startsWith("4.3") || fhirVersionSource.startsWith("5.0")) && !fhirVersionSource.equals(this.getVersion().substring(0,3))) {
log.info("Loading additional FHIR version for Source into context " + fhirVersionSource);
context = getContextForFhirVersion(fhirVersionSource);
}
org.hl7.fhir.r5.elementmodel.Element src = Manager.parseSingle(context, new ByteArrayInputStream(source.getBytes()),
Expand All @@ -431,24 +489,47 @@ public SimpleWorkerContext getContextForFhirVersion(String fhirVersion)
throws FHIRException, IOException {
SimpleWorkerContext contextForFhirVersion = null;
if (fhirVersion.startsWith("4.0")) {
contextForFhirVersion = new SimpleWorkerContextBuilder().fromPackage(NpmPackage.fromPackage(getClass().getResourceAsStream("/hl7.fhir.r4.core.tgz")));
ValidationEngine engine = sessionCache.fetchSessionValidatorEngine(fhirVersion.substring(0,3));
if (engine == null) {
engine = new MatchboxEngineBuilder().getEngineR4();
sessionCache.cacheSession(fhirVersion.substring(0,3), engine);
}
contextForFhirVersion = engine.getContext();
}
if (fhirVersion.startsWith("4.3")) {
ValidationEngine engine = sessionCache.fetchSessionValidatorEngine(fhirVersion.substring(0,3));
if (engine == null) {
engine = new MatchboxEngineBuilder().getEngineR4B();
sessionCache.cacheSession(fhirVersion.substring(0,3), engine);
}
contextForFhirVersion = engine.getContext();
}
if (fhirVersion.startsWith("5.0")) {
contextForFhirVersion = new SimpleWorkerContextBuilder().fromPackage(NpmPackage.fromPackage(getClass().getResourceAsStream("/hl7.fhir.r5.core.tgz")));
}
ValidationEngine engine = sessionCache.fetchSessionValidatorEngine(fhirVersion.substring(0,3));
if (engine == null) {
engine = new MatchboxEngineBuilder().getEngineR5();
sessionCache.cacheSession(fhirVersion.substring(0,3), engine);
}
contextForFhirVersion = engine.getContext();
}
if (contextForFhirVersion != null) {
// we need to copy now all StructureDefinitions from this Version to the new context
for (StructureDefinition sd : contextForFhirVersion.listStructures()) {
StructureDefinition sdn = sd.copy();
if (sdn.getKind()!=null && sdn.getKind() != StructureDefinition.StructureDefinitionKind.LOGICAL && !"Extensions".equals(sdn.getType())) {
sdn.setUrl(sdn.getUrl().replace("http://hl7.org/fhir/", "http://hl7.org/fhir/"+fhirVersion+"/"));
sdn.addExtension().setUrl("http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace")
.setValue(new UriType("http://hl7.org/fhir"));
this.getContext().cacheResource(sdn);
}
}
// check first if they are not already defined
if (this.getContext().fetchResource(StructureDefinition.class, "http://hl7.org/fhir/"+fhirVersion.substring(0,3)+"/StructureDefinition/StructureDefinition") == null) {
int len = "http://hl7.org/fhir/".length();
for (StructureDefinition sd : contextForFhirVersion.listStructures()) {
if (sd.getKind()!=null && sd.getKind() != StructureDefinition.StructureDefinitionKind.LOGICAL && !"Extensions".equals(sd.getType())) {
if (!Character.isDigit(sd.getUrl().charAt(len))) {
StructureDefinition sdn = sd.copy();
sdn.setUrl(sdn.getUrl().replace("http://hl7.org/fhir/", "http://hl7.org/fhir/"+fhirVersion.substring(0,3)+"/"));
sdn.addExtension().setUrl("http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace")
.setValue(new UriType("http://hl7.org/fhir"));
this.getContext().cacheResource(sdn);
}
}
}
}
}

return contextForFhirVersion;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ protected InputStreamWithSrc loadFromPackageServer(String id, String version) {

// matchbox-engine PATCH, we do not want to load from a package server for hl7.fhir.xver-extension :
if (CommonPackages.ID_XVER.equals(id)) {
ourLog.info("loading " +id+ " form classpath");
ourLog.info("loading " +id+ " from classpath");
version = VER_XVER_PROVIDED;
InputStream stream = getClass().getResourceAsStream("/"+id+"#"+version+".tgz");
if (stream==null) {
Expand All @@ -342,8 +342,8 @@ protected InputStreamWithSrc loadFromPackageServer(String id, String version) {
return new InputStreamWithSrc(stream, "http://fhir.org/packages/hl7.fhir.xver-extensions", version);
}

if ("hl7.fhir.r5.core".equals(id)) {
ourLog.info("loading hl7.fhir.r5.core form classpath");
if (id.startsWith("hl7.fhir") && id.endsWith("core")) {
ourLog.info("loading from classpath "+id);
InputStream stream = getClass().getResourceAsStream("/"+id+".tgz");
if (stream==null) {
ourLog.error("Unable to find/resolve/read from classpath (we dont' want go to the package server) for :" + id+"#"+version+".tgz");
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,22 @@
*/
class FhirXVersTests {

static private MatchboxEngine engine;
static private MatchboxEngine engineR4B, engineR4, engineR5;

private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(FhirMappingLanguageTests.class);

@BeforeAll
static void setUpBeforeClass() throws Exception {
engine = new MatchboxEngineBuilder().getEngineR4();
engineR4 = new MatchboxEngineBuilder().getEngineR4();
engineR4B = new MatchboxEngineBuilder().getEngineR4B();
engineR5 = new MatchboxEngineBuilder().getEngineR5();
// optional, just for peformance reason
engineR4.cacheXVersionEngine(engineR4B);
engineR4.cacheXVersionEngine(engineR5);
engineR4B.cacheXVersionEngine(engineR4);
engineR4B.cacheXVersionEngine(engineR5);
engineR5.cacheXVersionEngine(engineR4);
engineR5.cacheXVersionEngine(engineR4B);
}

@BeforeEach
Expand All @@ -76,21 +85,90 @@ public StructureDefinition getStructureDefinitionFromFile(String file) throws IO

@Test
void testMedication5to4inR4() throws FHIRException, IOException {
MatchboxEngine engine = new MatchboxEngine(FhirXVersTests.engine);
MatchboxEngine engine = FhirXVersTests.engineR4;
String result = engine.transform(getFileAsStringFromResources("/medication-r5-med0301.json"), true,
"http://hl7.org/fhir/uv/xver/StructureMap/Medication5to4", true);
log.info(result);
CompareUtil.compare(getFileAsStringFromResources("/medication-r4-med0301.json"), result, false);
}

@Test
void testMedication5to4inR4B() throws FHIRException, IOException {
MatchboxEngine engine =FhirXVersTests.engineR4B;
String result = engine.transform(getFileAsStringFromResources("/medication-r5-med0301.json"), true,
"http://hl7.org/fhir/uv/xver/StructureMap/Medication5to4", true);
log.info(result);
CompareUtil.compare(getFileAsStringFromResources("/medication-r4-med0301.json"), result, false);
}

@Test
void testMedication5to4BinR4B() throws FHIRException, IOException {
MatchboxEngine engine =FhirXVersTests.engineR4B;
String result = engine.transform(getFileAsStringFromResources("/medication-r5-med0301.json"), true,
"http://hl7.org/fhir/uv/xver/StructureMap/Medication5to4b", true);
log.info(result);
CompareUtil.compare(getFileAsStringFromResources("/medication-r4b-med0301.json"), result, false);
}

@Test
void testMedication5to4inR5() throws FHIRException, IOException {
MatchboxEngine engine =FhirXVersTests.engineR5;
String result = engine.transform(getFileAsStringFromResources("/medication-r5-med0301.json"), true,
"http://hl7.org/fhir/uv/xver/StructureMap/Medication5to4", true);
log.info(result);
CompareUtil.compare(getFileAsStringFromResources("/medication-r4-med0301.json"), result, false);
}

@Test
void testMedication5to4BinR5() throws FHIRException, IOException {
MatchboxEngine engine =FhirXVersTests.engineR5;
String result = engine.transform(getFileAsStringFromResources("/medication-r5-med0301.json"), true,
"http://hl7.org/fhir/uv/xver/StructureMap/Medication5to4b", true);
log.info(result);
CompareUtil.compare(getFileAsStringFromResources("/medication-r4b-med0301.json"), result, false);
}

@Test
void testMedication4to5inR4() throws FHIRException, IOException {
MatchboxEngine engine = new MatchboxEngine(FhirXVersTests.engine);
MatchboxEngine engine =FhirXVersTests.engineR4;
String result = engine.transform(getFileAsStringFromResources("/medication-r4-med0301.json"), true,
"http://hl7.org/fhir/uv/xver/StructureMap/Medication4to5", true);
log.info(result);
CompareUtil.compare(getFileAsStringFromResources("/medication-r5-med0301.json"), result, false);
}

@Test
void testMedication4to5inR4B() throws FHIRException, IOException {
MatchboxEngine engine =FhirXVersTests.engineR4B;
String result = engine.transform(getFileAsStringFromResources("/medication-r4-med0301.json"), true,
"http://hl7.org/fhir/uv/xver/StructureMap/Medication4to5", true);
CompareUtil.compare(getFileAsStringFromResources("/medication-r5-med0301.json"), result, false);
}

@Test
void testMedication4Bto5inR4B() throws FHIRException, IOException {
MatchboxEngine engine =FhirXVersTests.engineR4B;
String result = engine.transform(getFileAsStringFromResources("/medication-r4b-med0301.json"), true,
"http://hl7.org/fhir/uv/xver/StructureMap/Medication4Bto5", true);
CompareUtil.compare(getFileAsStringFromResources("/medication-r5-med0301.json"), result, false);
}

@Test
void testMedication4to5inR5() throws FHIRException, IOException {
MatchboxEngine engine =FhirXVersTests.engineR5;
String result = engine.transform(getFileAsStringFromResources("/medication-r4-med0301.json"), true,
"http://hl7.org/fhir/uv/xver/StructureMap/Medication4to5", true);
log.info(result);
CompareUtil.compare(getFileAsStringFromResources("/medication-r5-med0301.json"), result, false);
}

@Test
void testMedication4Bto5inR5() throws FHIRException, IOException {
MatchboxEngine engine =FhirXVersTests.engineR5;
String result = engine.transform(getFileAsStringFromResources("/medication-r4b-med0301.json"), true,
"http://hl7.org/fhir/uv/xver/StructureMap/Medication4Bto5", true);
CompareUtil.compare(getFileAsStringFromResources("/medication-r5-med0301.json"), result, false);
}


}
Loading

0 comments on commit 578de87

Please sign in to comment.