From 21037e2cdf883ca0839edab598f9f55b18a1d60c Mon Sep 17 00:00:00 2001 From: dotasek Date: Mon, 31 Oct 2022 14:22:46 -0400 Subject: [PATCH 1/7] Failing test --- .../fhir/r5/context/TerminologyCacheTests.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/TerminologyCacheTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/TerminologyCacheTests.java index ea46412e5..ad518025b 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/TerminologyCacheTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/TerminologyCacheTests.java @@ -238,6 +238,8 @@ public class TerminologyCacheTests implements ResourceLoaderTests { assertTrue(cacheToken.hasVersion()); } + + @Test public void testCodableConceptCacheTokenGeneration() throws IOException, URISyntaxException { @@ -453,4 +455,20 @@ public class TerminologyCacheTests implements ResourceLoaderTests { assertEquals("http://dummy.org", extracted); } + + @Test + public void testCodingWithSystemCacheTokenGenerationWithPipeCharSystem() throws IOException, URISyntaxException { + + TerminologyCache terminologyCache = createTerminologyCache(); + ValueSet valueSet = new ValueSet(); + + Coding coding = new Coding(); + coding.setCode("dummyCode"); + coding.setSystem("http://terminology.hl7.org/CodeSystem/dummy|System"); + coding.setVersion("dummyVersion"); + TerminologyCache.CacheToken cacheToken = terminologyCache.generateValidationToken(CacheTestUtils.validationOptions, + coding, valueSet); + assertEquals("dummyXSystem", cacheToken.getName()); + assertTrue(cacheToken.hasVersion()); + } } From 942777da1117eebc41ffb493f0dc08781b4191ab Mon Sep 17 00:00:00 2001 From: dotasek Date: Mon, 31 Oct 2022 14:27:03 -0400 Subject: [PATCH 2/7] Fix token name for code system --- .../org/hl7/fhir/r5/context/TerminologyCache.java | 2 +- .../org.hl7.fhir.validation/5.0.0/v2-0360X2.7.cache | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/v2-0360X2.7.cache diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java index 287d1cb9b..4ea977d42 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java @@ -320,7 +320,7 @@ public class TerminologyCache { if (system.startsWith("urn:iso:std:iso:")) return "iso"+system.substring(16).replace(":", ""); if (system.startsWith("http://terminology.hl7.org/CodeSystem/")) - return system.substring(38).replace("/", ""); + return system.substring(38).replace("/", "").replace('|','X'); if (system.startsWith("http://hl7.org/fhir/")) return system.substring(20).replace("/", ""); if (system.equals("urn:ietf:bcp:47")) diff --git a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/v2-0360X2.7.cache b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/v2-0360X2.7.cache new file mode 100644 index 000000000..00f2436f5 --- /dev/null +++ b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/v2-0360X2.7.cache @@ -0,0 +1,12 @@ +------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://terminology.hl7.org/CodeSystem/v2-0360|2.7", + "code" : "BS", + "display" : "Bachelor of Science" +}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"true"}#### +v: { + "display" : "Bachelor of Science", + "code" : "BS", + "system" : "http://terminology.hl7.org/CodeSystem/v2-0360|2.7" +} +------------------------------------------------------------------------------------- From 5558f0458f88a05c3895a31403bb5492c471319e Mon Sep 17 00:00:00 2001 From: dotasek Date: Mon, 31 Oct 2022 14:53:50 -0400 Subject: [PATCH 3/7] Refactor --- .../hl7/fhir/r5/context/TerminologyCache.java | 84 +++++++++++-------- 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java index 4ea977d42..9556d1ebd 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java @@ -38,7 +38,6 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.util.*; -import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; @@ -85,6 +84,8 @@ public class TerminologyCache { private static final String CAPABILITY_STATEMENT_TITLE = ".capabilityStatement"; private static final String TERMINOLOGY_CAPABILITIES_TITLE = ".terminologyCapabilities"; + private SystemNameKeyGenerator systemNameKeyGenerator = new SystemNameKeyGenerator(); + public class CacheToken { @Getter private String name; @@ -96,13 +97,48 @@ public class TerminologyCache { private boolean hasVersion; public void setName(String n) { + String systemName = getSystemNameKeyGenerator().getNameForSystem(n); if (name == null) - name = n; - else if (!n.equals(name)) + name = systemName; + else if (!systemName.equals(name)) name = NAME_FOR_NO_SYSTEM; } } + protected SystemNameKeyGenerator getSystemNameKeyGenerator() { + return systemNameKeyGenerator; + } + public class SystemNameKeyGenerator { + public String getNameForSystem(String system) { + if (system.equals("http://snomed.info/sct")) + return "snomed"; + if (system.equals("http://www.nlm.nih.gov/research/umls/rxnorm")) + return "rxnorm"; + if (system.equals("http://loinc.org")) + return "loinc"; + if (system.equals("http://unitsofmeasure.org")) + return "ucum"; + if (system.startsWith("http://hl7.org/fhir/sid/")) + return system.substring(24).replace("/", ""); + if (system.startsWith("urn:iso:std:iso:")) + return "iso"+system.substring(16).replace(":", ""); + if (system.startsWith("http://terminology.hl7.org/CodeSystem/")) + return system.substring(38).replace("/", "").replace('|','X'); + if (system.startsWith("http://hl7.org/fhir/")) + return system.substring(20).replace("/", ""); + if (system.equals("urn:ietf:bcp:47")) + return "lang"; + if (system.equals("urn:ietf:bcp:13")) + return "mimetypes"; + if (system.equals("urn:iso:std:iso:11073:10101")) + return "11073"; + if (system.equals("http://dicom.nema.org/resources/ontology/DCM")) + return "dicom"; + return system.replace("/", "_").replace(":", "_").replace("?", "X").replace("#", "X"); + } + } + + private class CacheEntry { private String request; private boolean persistent; @@ -185,7 +221,7 @@ public class TerminologyCache { public CacheToken generateValidationToken(ValidationOptions options, Coding code, ValueSet vs) { CacheToken ct = new CacheToken(); if (code.hasSystem()) { - ct.name = getNameForSystem(code.getSystem()); + ct.setName(code.getSystem()); ct.hasVersion = code.hasVersion(); } else @@ -226,7 +262,7 @@ public class TerminologyCache { CacheToken ct = new CacheToken(); for (Coding c : code.getCoding()) { if (c.hasSystem()) { - ct.setName(getNameForSystem(c.getSystem())); + ct.setName(c.getSystem()); ct.hasVersion = c.hasVersion(); } } @@ -287,53 +323,31 @@ public class TerminologyCache { if (vs != null) { for (ConceptSetComponent inc : vs.getCompose().getInclude()) { if (inc.hasSystem()) { - ct.setName(getNameForSystem(inc.getSystem())); + ct.setName(inc.getSystem()); ct.hasVersion = inc.hasVersion(); } } for (ConceptSetComponent inc : vs.getCompose().getExclude()) { if (inc.hasSystem()) { - ct.setName(getNameForSystem(inc.getSystem())); + ct.setName(inc.getSystem()); ct.hasVersion = inc.hasVersion(); } } for (ValueSetExpansionContainsComponent inc : vs.getExpansion().getContains()) { if (inc.hasSystem()) { - ct.setName(getNameForSystem(inc.getSystem())); + ct.setName(inc.getSystem()); ct.hasVersion = inc.hasVersion(); } } } } - private String getNameForSystem(String system) { - if (system.equals("http://snomed.info/sct")) - return "snomed"; - if (system.equals("http://www.nlm.nih.gov/research/umls/rxnorm")) - return "rxnorm"; - if (system.equals("http://loinc.org")) - return "loinc"; - if (system.equals("http://unitsofmeasure.org")) - return "ucum"; - if (system.startsWith("http://hl7.org/fhir/sid/")) - return system.substring(24).replace("/", ""); - if (system.startsWith("urn:iso:std:iso:")) - return "iso"+system.substring(16).replace(":", ""); - if (system.startsWith("http://terminology.hl7.org/CodeSystem/")) - return system.substring(38).replace("/", "").replace('|','X'); - if (system.startsWith("http://hl7.org/fhir/")) - return system.substring(20).replace("/", ""); - if (system.equals("urn:ietf:bcp:47")) - return "lang"; - if (system.equals("urn:ietf:bcp:13")) - return "mimetypes"; - if (system.equals("urn:iso:std:iso:11073:10101")) - return "11073"; - if (system.equals("http://dicom.nema.org/resources/ontology/DCM")) - return "dicom"; - return system.replace("/", "_").replace(":", "_").replace("?", "X").replace("#", "X"); + private String normalizeSystemPath(String path) { + return path.replace("/", "").replace('|','X'); } + + public NamedCache getNamedCache(CacheToken cacheToken) { final String cacheName = cacheToken.name == null ? "null" : cacheToken.name; @@ -687,7 +701,7 @@ public class TerminologyCache { public void removeCS(String url) { synchronized (lock) { - String name = getNameForSystem(url); + String name = getSystemNameKeyGenerator().getNameForSystem(url); if (caches.containsKey(name)) { caches.remove(name); } From 16a0a08598104f8bc49ff00c0141910e65995432 Mon Sep 17 00:00:00 2001 From: dotasek Date: Wed, 2 Nov 2022 16:58:28 -0400 Subject: [PATCH 4/7] Parse out canonical url version + refactor --- .../hl7/fhir/r5/context/TerminologyCache.java | 62 ++++++++++++++----- .../r5/context/TerminologyCacheTests.java | 18 +++--- .../{v2-0360X2.7.cache => v2-0360_2.7.cache} | 0 3 files changed, 57 insertions(+), 23 deletions(-) rename org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/{v2-0360X2.7.cache => v2-0360_2.7.cache} (100%) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java index 9556d1ebd..18c981b6e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java @@ -84,6 +84,7 @@ public class TerminologyCache { private static final String CAPABILITY_STATEMENT_TITLE = ".capabilityStatement"; private static final String TERMINOLOGY_CAPABILITIES_TITLE = ".terminologyCapabilities"; + private SystemNameKeyGenerator systemNameKeyGenerator = new SystemNameKeyGenerator(); public class CacheToken { @@ -109,33 +110,62 @@ public class TerminologyCache { return systemNameKeyGenerator; } public class SystemNameKeyGenerator { + public static final String SNOMED_SCT_CODESYSTEM_URL = "http://snomed.info/sct"; + public static final String RXNORM_CODESYSTEM_URL = "http://www.nlm.nih.gov/research/umls/rxnorm"; + public static final String LOINC_CODESYSTEM_URL = "http://loinc.org"; + public static final String UCUM_CODESYSTEM_URL = "http://unitsofmeasure.org"; + + public static final String HL7_TERMINOLOGY_CODESYSTEM_BASE_URL = "http://terminology.hl7.org/CodeSystem/"; + public static final String HL7_SID_CODESYSTEM_BASE_URL = "http://hl7.org/fhir/sid/"; + public static final String HL7_FHIR_CODESYSTEM_BASE_URL = "http://hl7.org/fhir/"; + + public static final String ISO_CODESYSTEM_URN = "urn:iso:std:iso:"; + public static final String LANG_CODESYSTEM_URN = "urn:ietf:bcp:47"; + public static final String MIMETYPES_CODESYSTEM_URN = "urn:ietf:bcp:13"; + + public static final String _11073_CODESYSTEM_URN = "urn:iso:std:iso:11073:10101"; + public static final String DICOM_CODESYSTEM_URL = "http://dicom.nema.org/resources/ontology/DCM"; + + public String getNameForSystem(String system) { - if (system.equals("http://snomed.info/sct")) + System.out.println(system); + if (system.equals(SNOMED_SCT_CODESYSTEM_URL)) return "snomed"; - if (system.equals("http://www.nlm.nih.gov/research/umls/rxnorm")) + if (system.equals(RXNORM_CODESYSTEM_URL)) return "rxnorm"; - if (system.equals("http://loinc.org")) + if (system.equals(LOINC_CODESYSTEM_URL)) return "loinc"; - if (system.equals("http://unitsofmeasure.org")) + if (system.equals(UCUM_CODESYSTEM_URL)) return "ucum"; - if (system.startsWith("http://hl7.org/fhir/sid/")) - return system.substring(24).replace("/", ""); - if (system.startsWith("urn:iso:std:iso:")) - return "iso"+system.substring(16).replace(":", ""); - if (system.startsWith("http://terminology.hl7.org/CodeSystem/")) - return system.substring(38).replace("/", "").replace('|','X'); - if (system.startsWith("http://hl7.org/fhir/")) - return system.substring(20).replace("/", ""); - if (system.equals("urn:ietf:bcp:47")) + if (system.startsWith(HL7_SID_CODESYSTEM_BASE_URL)) + return getNameForCanonicalURL(HL7_SID_CODESYSTEM_BASE_URL, system); + if (system.startsWith(ISO_CODESYSTEM_URN)) + return "iso"+system.substring(ISO_CODESYSTEM_URN.length()).replace(":", ""); + if (system.startsWith(HL7_TERMINOLOGY_CODESYSTEM_BASE_URL)) + return getNameForCanonicalURL(HL7_TERMINOLOGY_CODESYSTEM_BASE_URL, system); + if (system.startsWith(HL7_FHIR_CODESYSTEM_BASE_URL)) + return getNameForCanonicalURL(HL7_FHIR_CODESYSTEM_BASE_URL, system); + if (system.equals(LANG_CODESYSTEM_URN)) return "lang"; - if (system.equals("urn:ietf:bcp:13")) + if (system.equals(MIMETYPES_CODESYSTEM_URN)) return "mimetypes"; - if (system.equals("urn:iso:std:iso:11073:10101")) + if (system.equals(_11073_CODESYSTEM_URN)) return "11073"; - if (system.equals("http://dicom.nema.org/resources/ontology/DCM")) + if (system.equals(DICOM_CODESYSTEM_URL)) return "dicom"; return system.replace("/", "_").replace(":", "_").replace("?", "X").replace("#", "X"); } + + public String getNameForCanonicalURL(String baseUrl, String fullUrl) { + final String[] subIds = fullUrl.substring(baseUrl.length()).split("\\|"); + + String baseId = subIds[0].replace("/", ""); + if (subIds.length == 2) { + return baseId + "_" + subIds[1]; + } + return baseId; + + } } diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/TerminologyCacheTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/TerminologyCacheTests.java index ad518025b..6b28737e2 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/TerminologyCacheTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/TerminologyCacheTests.java @@ -33,6 +33,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.MethodSource; import com.google.gson.JsonElement; @@ -456,19 +457,22 @@ public class TerminologyCacheTests implements ResourceLoaderTests { assertEquals("http://dummy.org", extracted); } - @Test - public void testCodingWithSystemCacheTokenGenerationWithPipeCharSystem() throws IOException, URISyntaxException { + @ParameterizedTest + @CsvSource({ + "http://terminology.hl7.org/CodeSystem/id|version,id_version", + "http://hl7.org/fhir/id|version,id_version", + "http://hl7.org/fhir/sid/id|version,id_version" + }) + public void testCacheTokenGenerationWithCanonicalUrl(String system, String expectedName) throws IOException, URISyntaxException { TerminologyCache terminologyCache = createTerminologyCache(); ValueSet valueSet = new ValueSet(); Coding coding = new Coding(); - coding.setCode("dummyCode"); - coding.setSystem("http://terminology.hl7.org/CodeSystem/dummy|System"); - coding.setVersion("dummyVersion"); + coding.setSystem(system); TerminologyCache.CacheToken cacheToken = terminologyCache.generateValidationToken(CacheTestUtils.validationOptions, coding, valueSet); - assertEquals("dummyXSystem", cacheToken.getName()); - assertTrue(cacheToken.hasVersion()); + assertEquals(expectedName, cacheToken.getName()); + } } diff --git a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/v2-0360X2.7.cache b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/v2-0360_2.7.cache similarity index 100% rename from org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/v2-0360X2.7.cache rename to org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/v2-0360_2.7.cache From 6ecd4c70fe0885dfc15dd62fdeebfa0b606732ca Mon Sep 17 00:00:00 2001 From: dotasek Date: Thu, 3 Nov 2022 14:29:38 -0400 Subject: [PATCH 5/7] Parse out version for all systems in TerminologyCache files --- .../hl7/fhir/r5/context/TerminologyCache.java | 70 ++++++++++--------- .../r5/context/TerminologyCacheTests.java | 31 +++++--- 2 files changed, 56 insertions(+), 45 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java index 18c981b6e..2cfcc8ca6 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java @@ -126,45 +126,47 @@ public class TerminologyCache { public static final String _11073_CODESYSTEM_URN = "urn:iso:std:iso:11073:10101"; public static final String DICOM_CODESYSTEM_URL = "http://dicom.nema.org/resources/ontology/DCM"; - public String getNameForSystem(String system) { - System.out.println(system); - if (system.equals(SNOMED_SCT_CODESYSTEM_URL)) - return "snomed"; - if (system.equals(RXNORM_CODESYSTEM_URL)) - return "rxnorm"; - if (system.equals(LOINC_CODESYSTEM_URL)) - return "loinc"; - if (system.equals(UCUM_CODESYSTEM_URL)) - return "ucum"; - if (system.startsWith(HL7_SID_CODESYSTEM_BASE_URL)) - return getNameForCanonicalURL(HL7_SID_CODESYSTEM_BASE_URL, system); - if (system.startsWith(ISO_CODESYSTEM_URN)) - return "iso"+system.substring(ISO_CODESYSTEM_URN.length()).replace(":", ""); - if (system.startsWith(HL7_TERMINOLOGY_CODESYSTEM_BASE_URL)) - return getNameForCanonicalURL(HL7_TERMINOLOGY_CODESYSTEM_BASE_URL, system); - if (system.startsWith(HL7_FHIR_CODESYSTEM_BASE_URL)) - return getNameForCanonicalURL(HL7_FHIR_CODESYSTEM_BASE_URL, system); - if (system.equals(LANG_CODESYSTEM_URN)) - return "lang"; - if (system.equals(MIMETYPES_CODESYSTEM_URN)) - return "mimetypes"; - if (system.equals(_11073_CODESYSTEM_URN)) - return "11073"; - if (system.equals(DICOM_CODESYSTEM_URL)) - return "dicom"; - return system.replace("/", "_").replace(":", "_").replace("?", "X").replace("#", "X"); + final int lastPipe = system.lastIndexOf('|'); + final String systemBaseName = lastPipe == -1 ? system : system.substring(0,lastPipe); + final String systemVersion = lastPipe == -1 ? null : system.substring(lastPipe + 1); + + if (systemBaseName.equals(SNOMED_SCT_CODESYSTEM_URL)) + return getVersionedSystem("snomed", systemVersion); + if (systemBaseName.equals(RXNORM_CODESYSTEM_URL)) + return getVersionedSystem("rxnorm", systemVersion); + if (systemBaseName.equals(LOINC_CODESYSTEM_URL)) + return getVersionedSystem("loinc", systemVersion); + if (systemBaseName.equals(UCUM_CODESYSTEM_URL)) + return getVersionedSystem("ucum", systemVersion); + if (systemBaseName.startsWith(HL7_SID_CODESYSTEM_BASE_URL)) + return getVersionedSystem(normalizeBaseURL(HL7_SID_CODESYSTEM_BASE_URL, systemBaseName), systemVersion); + if (systemBaseName.startsWith(ISO_CODESYSTEM_URN)) + return getVersionedSystem("iso"+systemBaseName.substring(ISO_CODESYSTEM_URN.length()).replace(":", ""), systemVersion); + if (systemBaseName.startsWith(HL7_TERMINOLOGY_CODESYSTEM_BASE_URL)) + return getVersionedSystem(normalizeBaseURL(HL7_TERMINOLOGY_CODESYSTEM_BASE_URL, systemBaseName), systemVersion); + if (systemBaseName.startsWith(HL7_FHIR_CODESYSTEM_BASE_URL)) + return getVersionedSystem(normalizeBaseURL(HL7_FHIR_CODESYSTEM_BASE_URL, systemBaseName), systemVersion); + if (systemBaseName.equals(LANG_CODESYSTEM_URN)) + return getVersionedSystem("lang", systemVersion); + if (systemBaseName.equals(MIMETYPES_CODESYSTEM_URN)) + return getVersionedSystem("mimetypes", systemVersion); + if (systemBaseName.equals(_11073_CODESYSTEM_URN)) + return getVersionedSystem("11073", systemVersion); + if (systemBaseName.equals(DICOM_CODESYSTEM_URL)) + return getVersionedSystem("dicom", systemVersion); + return getVersionedSystem(systemBaseName.replace("/", "_").replace(":", "_").replace("?", "X").replace("#", "X"), systemVersion); } - public String getNameForCanonicalURL(String baseUrl, String fullUrl) { - final String[] subIds = fullUrl.substring(baseUrl.length()).split("\\|"); + public String normalizeBaseURL(String baseUrl, String fullUrl) { + return fullUrl.substring(baseUrl.length()).replace("/", ""); + } - String baseId = subIds[0].replace("/", ""); - if (subIds.length == 2) { - return baseId + "_" + subIds[1]; + public String getVersionedSystem(String baseSystem, String version) { + if (version != null) { + return baseSystem + "_" + version; } - return baseId; - + return baseSystem; } } diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/TerminologyCacheTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/TerminologyCacheTests.java index 6b28737e2..26909b687 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/TerminologyCacheTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/TerminologyCacheTests.java @@ -459,20 +459,29 @@ public class TerminologyCacheTests implements ResourceLoaderTests { @ParameterizedTest @CsvSource({ - "http://terminology.hl7.org/CodeSystem/id|version,id_version", - "http://hl7.org/fhir/id|version,id_version", - "http://hl7.org/fhir/sid/id|version,id_version" + "http://terminology.hl7.org/CodeSystem/id,id", + "http://hl7.org/fhir/id,id", + "http://hl7.org/fhir/sid/id,id", + "http://www.nlm.nih.gov/research/umls/rxnorm,rxnorm", + "http://snomed.info/sct,snomed", }) - public void testCacheTokenGenerationWithCanonicalUrl(String system, String expectedName) throws IOException, URISyntaxException { + public void testCacheTokenGeneration(String system, String expectedName) throws IOException, URISyntaxException { TerminologyCache terminologyCache = createTerminologyCache(); ValueSet valueSet = new ValueSet(); - - Coding coding = new Coding(); - coding.setSystem(system); - TerminologyCache.CacheToken cacheToken = terminologyCache.generateValidationToken(CacheTestUtils.validationOptions, - coding, valueSet); - assertEquals(expectedName, cacheToken.getName()); - + { + Coding coding = new Coding(); + coding.setSystem(system); + TerminologyCache.CacheToken cacheToken = terminologyCache.generateValidationToken(CacheTestUtils.validationOptions, + coding, valueSet); + assertEquals(expectedName, cacheToken.getName()); + } + { + Coding coding = new Coding(); + coding.setSystem(system + "|dummyVersion"); + TerminologyCache.CacheToken cacheToken = terminologyCache.generateValidationToken(CacheTestUtils.validationOptions, + coding, valueSet); + assertEquals(expectedName + "_dummyVersion", cacheToken.getName()); + } } } From 99bd733fe0a3b4fd287fa910205c2468f136e934 Mon Sep 17 00:00:00 2001 From: dotasek Date: Thu, 3 Nov 2022 16:33:56 -0400 Subject: [PATCH 6/7] Update tests + fix 11073 codesystem --- .../java/org/hl7/fhir/r5/context/TerminologyCache.java | 4 ++-- .../org/hl7/fhir/r5/context/TerminologyCacheTests.java | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java index 2cfcc8ca6..4ec3c1452 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java @@ -141,6 +141,8 @@ public class TerminologyCache { return getVersionedSystem("ucum", systemVersion); if (systemBaseName.startsWith(HL7_SID_CODESYSTEM_BASE_URL)) return getVersionedSystem(normalizeBaseURL(HL7_SID_CODESYSTEM_BASE_URL, systemBaseName), systemVersion); + if (systemBaseName.equals(_11073_CODESYSTEM_URN)) + return getVersionedSystem("11073", systemVersion); if (systemBaseName.startsWith(ISO_CODESYSTEM_URN)) return getVersionedSystem("iso"+systemBaseName.substring(ISO_CODESYSTEM_URN.length()).replace(":", ""), systemVersion); if (systemBaseName.startsWith(HL7_TERMINOLOGY_CODESYSTEM_BASE_URL)) @@ -151,8 +153,6 @@ public class TerminologyCache { return getVersionedSystem("lang", systemVersion); if (systemBaseName.equals(MIMETYPES_CODESYSTEM_URN)) return getVersionedSystem("mimetypes", systemVersion); - if (systemBaseName.equals(_11073_CODESYSTEM_URN)) - return getVersionedSystem("11073", systemVersion); if (systemBaseName.equals(DICOM_CODESYSTEM_URL)) return getVersionedSystem("dicom", systemVersion); return getVersionedSystem(systemBaseName.replace("/", "_").replace(":", "_").replace("?", "X").replace("#", "X"), systemVersion); diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/TerminologyCacheTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/TerminologyCacheTests.java index 26909b687..116eff7e5 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/TerminologyCacheTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/TerminologyCacheTests.java @@ -464,6 +464,15 @@ public class TerminologyCacheTests implements ResourceLoaderTests { "http://hl7.org/fhir/sid/id,id", "http://www.nlm.nih.gov/research/umls/rxnorm,rxnorm", "http://snomed.info/sct,snomed", + "http://www.nlm.nih.gov/research/umls/rxnorm,rxnorm", + "http://loinc.org,loinc", + "http://unitsofmeasure.org,ucum", + "urn:iso:std:iso:id,isoid", + "urn:ietf:bcp:47,lang", + "urn:ietf:bcp:13,mimetypes", + "urn:iso:std:iso:11073:10101,11073", + "my://random/system?with#chars,my___random_systemXwithXchars", + "http://dicom.nema.org/resources/ontology/DCM,dicom" }) public void testCacheTokenGeneration(String system, String expectedName) throws IOException, URISyntaxException { From 14fdb713d9a4ee4d2cdf429546df39de630b472b Mon Sep 17 00:00:00 2001 From: dotasek Date: Thu, 3 Nov 2022 17:47:48 -0400 Subject: [PATCH 7/7] Backport TerminologyCache changes to r4, r4b --- .../hl7/fhir/r4/context/TerminologyCache.java | 170 ++++++++++------- .../hl7/fhir/r4/context/CacheTestUtils.java | 8 + .../r4/context/TerminologyCacheTests.java | 61 +++++++ .../fhir/r4b/context/TerminologyCache.java | 172 +++++++++++------- .../hl7/fhir/r4b/context/CacheTestUtils.java | 8 + .../r4b/context/TerminologyCacheTests.java | 68 +++++++ 6 files changed, 361 insertions(+), 126 deletions(-) create mode 100644 org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/context/CacheTestUtils.java create mode 100644 org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/context/TerminologyCacheTests.java create mode 100644 org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/context/CacheTestUtils.java create mode 100644 org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/context/TerminologyCacheTests.java diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/TerminologyCache.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/TerminologyCache.java index acd05391e..ae66e4b1d 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/TerminologyCache.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/TerminologyCache.java @@ -1,33 +1,33 @@ package org.hl7.fhir.r4.context; -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ @@ -84,16 +84,88 @@ public class TerminologyCache { private static final String ENTRY_MARKER = "-------------------------------------------------------------------------------------"; private static final String BREAK = "####"; + private SystemNameKeyGenerator systemNameKeyGenerator = new SystemNameKeyGenerator(); + + protected SystemNameKeyGenerator getSystemNameKeyGenerator() { + return systemNameKeyGenerator; + } + + public class SystemNameKeyGenerator { + public static final String SNOMED_SCT_CODESYSTEM_URL = "http://snomed.info/sct"; + public static final String RXNORM_CODESYSTEM_URL = "http://www.nlm.nih.gov/research/umls/rxnorm"; + public static final String LOINC_CODESYSTEM_URL = "http://loinc.org"; + public static final String UCUM_CODESYSTEM_URL = "http://unitsofmeasure.org"; + + public static final String HL7_TERMINOLOGY_CODESYSTEM_BASE_URL = "http://terminology.hl7.org/CodeSystem/"; + public static final String HL7_SID_CODESYSTEM_BASE_URL = "http://hl7.org/fhir/sid/"; + public static final String HL7_FHIR_CODESYSTEM_BASE_URL = "http://hl7.org/fhir/"; + + public static final String ISO_CODESYSTEM_URN = "urn:iso:std:iso:"; + public static final String LANG_CODESYSTEM_URN = "urn:ietf:bcp:47"; + public static final String MIMETYPES_CODESYSTEM_URN = "urn:ietf:bcp:13"; + + public static final String _11073_CODESYSTEM_URN = "urn:iso:std:iso:11073:10101"; + public static final String DICOM_CODESYSTEM_URL = "http://dicom.nema.org/resources/ontology/DCM"; + + public String getNameForSystem(String system) { + final int lastPipe = system.lastIndexOf('|'); + final String systemBaseName = lastPipe == -1 ? system : system.substring(0,lastPipe); + final String systemVersion = lastPipe == -1 ? null : system.substring(lastPipe + 1); + + if (systemBaseName.equals(SNOMED_SCT_CODESYSTEM_URL)) + return getVersionedSystem("snomed", systemVersion); + if (systemBaseName.equals(RXNORM_CODESYSTEM_URL)) + return getVersionedSystem("rxnorm", systemVersion); + if (systemBaseName.equals(LOINC_CODESYSTEM_URL)) + return getVersionedSystem("loinc", systemVersion); + if (systemBaseName.equals(UCUM_CODESYSTEM_URL)) + return getVersionedSystem("ucum", systemVersion); + if (systemBaseName.startsWith(HL7_SID_CODESYSTEM_BASE_URL)) + return getVersionedSystem(normalizeBaseURL(HL7_SID_CODESYSTEM_BASE_URL, systemBaseName), systemVersion); + if (systemBaseName.equals(_11073_CODESYSTEM_URN)) + return getVersionedSystem("11073", systemVersion); + if (systemBaseName.startsWith(ISO_CODESYSTEM_URN)) + return getVersionedSystem("iso"+systemBaseName.substring(ISO_CODESYSTEM_URN.length()).replace(":", ""), systemVersion); + if (systemBaseName.startsWith(HL7_TERMINOLOGY_CODESYSTEM_BASE_URL)) + return getVersionedSystem(normalizeBaseURL(HL7_TERMINOLOGY_CODESYSTEM_BASE_URL, systemBaseName), systemVersion); + if (systemBaseName.startsWith(HL7_FHIR_CODESYSTEM_BASE_URL)) + return getVersionedSystem(normalizeBaseURL(HL7_FHIR_CODESYSTEM_BASE_URL, systemBaseName), systemVersion); + if (systemBaseName.equals(LANG_CODESYSTEM_URN)) + return getVersionedSystem("lang", systemVersion); + if (systemBaseName.equals(MIMETYPES_CODESYSTEM_URN)) + return getVersionedSystem("mimetypes", systemVersion); + if (systemBaseName.equals(DICOM_CODESYSTEM_URL)) + return getVersionedSystem("dicom", systemVersion); + return getVersionedSystem(systemBaseName.replace("/", "_").replace(":", "_").replace("?", "X").replace("#", "X"), systemVersion); + } + + public String normalizeBaseURL(String baseUrl, String fullUrl) { + return fullUrl.substring(baseUrl.length()).replace("/", ""); + } + + public String getVersionedSystem(String baseSystem, String version) { + if (version != null) { + return baseSystem + "_" + version; + } + return baseSystem; + } + } + public class CacheToken { private String name; private String key; private String request; public void setName(String n) { + String systemName = getSystemNameKeyGenerator().getNameForSystem(n); if (name == null) - name = n; - else if (!n.equals(name)) + name = systemName; + else if (!systemName.equals(name)) name = NAME_FOR_NO_SYSTEM; } + + public String getName() { + return name; + } } private class CacheEntry { @@ -126,7 +198,7 @@ public class TerminologyCache { public CacheToken generateValidationToken(ValidationOptions options, Coding code, ValueSet vs) { CacheToken ct = new CacheToken(); if (code.hasSystem()) - ct.name = getNameForSystem(code.getSystem()); + ct.name = getSystemNameKeyGenerator().getNameForSystem(code.getSystem()); else ct.name = NAME_FOR_NO_SYSTEM; JsonParser json = new JsonParser(); @@ -145,7 +217,7 @@ public class TerminologyCache { CacheToken ct = new CacheToken(); for (Coding c : code.getCoding()) { if (c.hasSystem()) - ct.setName(getNameForSystem(c.getSystem())); + ct.setName(c.getSystem()); } JsonParser json = new JsonParser(); json.setOutputStyle(OutputStyle.PRETTY); @@ -176,13 +248,13 @@ public class TerminologyCache { ValueSet vsc = getVSEssense(vs); for (ConceptSetComponent inc : vs.getCompose().getInclude()) if (inc.hasSystem()) - ct.setName(getNameForSystem(inc.getSystem())); + ct.setName(inc.getSystem()); for (ConceptSetComponent inc : vs.getCompose().getExclude()) if (inc.hasSystem()) - ct.setName(getNameForSystem(inc.getSystem())); + ct.setName(inc.getSystem()); for (ValueSetExpansionContainsComponent inc : vs.getExpansion().getContains()) if (inc.hasSystem()) - ct.setName(getNameForSystem(inc.getSystem())); + ct.setName(inc.getSystem()); JsonParser json = new JsonParser(); json.setOutputStyle(OutputStyle.PRETTY); try { @@ -194,34 +266,6 @@ public class TerminologyCache { return ct; } - private String getNameForSystem(String system) { - if (system.equals("http://snomed.info/sct")) - return "snomed"; - if (system.equals("http://www.nlm.nih.gov/research/umls/rxnorm")) - return "rxnorm"; - if (system.equals("http://loinc.org")) - return "loinc"; - if (system.equals("http://unitsofmeasure.org")) - return "ucum"; - if (system.startsWith("http://hl7.org/fhir/sid/")) - return system.substring(24).replace("/", ""); - if (system.startsWith("urn:iso:std:iso:")) - return "iso"+system.substring(16).replace(":", ""); - if (system.startsWith("http://terminology.hl7.org/CodeSystem/")) - return system.substring(38).replace("/", ""); - if (system.startsWith("http://hl7.org/fhir/")) - return system.substring(20).replace("/", ""); - if (system.equals("urn:ietf:bcp:47")) - return "lang"; - if (system.equals("urn:ietf:bcp:13")) - return "mimetypes"; - if (system.equals("urn:iso:std:iso:11073:10101")) - return "11073"; - if (system.equals("http://dicom.nema.org/resources/ontology/DCM")) - return "dicom"; - return system.replace("/", "_").replace(":", "_"); - } - public NamedCache getNamedCache(CacheToken cacheToken) { NamedCache nc = caches.get(cacheToken.name); if (nc == null) { diff --git a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/context/CacheTestUtils.java b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/context/CacheTestUtils.java new file mode 100644 index 000000000..059d2ec0b --- /dev/null +++ b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/context/CacheTestUtils.java @@ -0,0 +1,8 @@ +package org.hl7.fhir.r4.context; + +import org.hl7.fhir.utilities.validation.ValidationOptions; + +public class CacheTestUtils { + public static final ValidationOptions validationOptions = new ValidationOptions().guessSystem().setVersionFlexible(false); + +} diff --git a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/context/TerminologyCacheTests.java b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/context/TerminologyCacheTests.java new file mode 100644 index 000000000..89ebbcc6c --- /dev/null +++ b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/context/TerminologyCacheTests.java @@ -0,0 +1,61 @@ +package org.hl7.fhir.r4.context; + + +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.ValueSet; + +import org.hl7.fhir.utilities.tests.ResourceLoaderTests; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import java.io.IOException; +import java.net.URISyntaxException; + +import static org.junit.jupiter.api.Assertions.assertEquals; + + +public class TerminologyCacheTests implements ResourceLoaderTests { + + private TerminologyCache createTerminologyCache() throws IOException { + Object lock = new Object(); + TerminologyCache terminologyCache = new TerminologyCache(lock, null); + return terminologyCache; + } + + @ParameterizedTest + @CsvSource({ + "http://terminology.hl7.org/CodeSystem/id,id", + "http://hl7.org/fhir/id,id", + "http://hl7.org/fhir/sid/id,id", + "http://www.nlm.nih.gov/research/umls/rxnorm,rxnorm", + "http://snomed.info/sct,snomed", + "http://www.nlm.nih.gov/research/umls/rxnorm,rxnorm", + "http://loinc.org,loinc", + "http://unitsofmeasure.org,ucum", + "urn:iso:std:iso:id,isoid", + "urn:ietf:bcp:47,lang", + "urn:ietf:bcp:13,mimetypes", + "urn:iso:std:iso:11073:10101,11073", + "my://random/system?with#chars,my___random_systemXwithXchars", + "http://dicom.nema.org/resources/ontology/DCM,dicom" + }) + public void testCacheTokenGeneration(String system, String expectedName) throws IOException, URISyntaxException { + + TerminologyCache terminologyCache = createTerminologyCache(); + ValueSet valueSet = new ValueSet(); + { + Coding coding = new Coding(); + coding.setSystem(system); + TerminologyCache.CacheToken cacheToken = terminologyCache.generateValidationToken(CacheTestUtils.validationOptions, + coding, valueSet); + assertEquals(expectedName, cacheToken.getName()); + } + { + Coding coding = new Coding(); + coding.setSystem(system + "|dummyVersion"); + TerminologyCache.CacheToken cacheToken = terminologyCache.generateValidationToken(CacheTestUtils.validationOptions, + coding, valueSet); + assertEquals(expectedName + "_dummyVersion", cacheToken.getName()); + } + } +} diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/context/TerminologyCache.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/context/TerminologyCache.java index fd6d48de4..4c5ce24b8 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/context/TerminologyCache.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/context/TerminologyCache.java @@ -1,33 +1,33 @@ package org.hl7.fhir.r4b.context; -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ @@ -84,16 +84,88 @@ public class TerminologyCache { private static final String ENTRY_MARKER = "-------------------------------------------------------------------------------------"; private static final String BREAK = "####"; + private SystemNameKeyGenerator systemNameKeyGenerator = new SystemNameKeyGenerator(); + + protected SystemNameKeyGenerator getSystemNameKeyGenerator() { + return systemNameKeyGenerator; + } + + public class SystemNameKeyGenerator { + public static final String SNOMED_SCT_CODESYSTEM_URL = "http://snomed.info/sct"; + public static final String RXNORM_CODESYSTEM_URL = "http://www.nlm.nih.gov/research/umls/rxnorm"; + public static final String LOINC_CODESYSTEM_URL = "http://loinc.org"; + public static final String UCUM_CODESYSTEM_URL = "http://unitsofmeasure.org"; + + public static final String HL7_TERMINOLOGY_CODESYSTEM_BASE_URL = "http://terminology.hl7.org/CodeSystem/"; + public static final String HL7_SID_CODESYSTEM_BASE_URL = "http://hl7.org/fhir/sid/"; + public static final String HL7_FHIR_CODESYSTEM_BASE_URL = "http://hl7.org/fhir/"; + + public static final String ISO_CODESYSTEM_URN = "urn:iso:std:iso:"; + public static final String LANG_CODESYSTEM_URN = "urn:ietf:bcp:47"; + public static final String MIMETYPES_CODESYSTEM_URN = "urn:ietf:bcp:13"; + + public static final String _11073_CODESYSTEM_URN = "urn:iso:std:iso:11073:10101"; + public static final String DICOM_CODESYSTEM_URL = "http://dicom.nema.org/resources/ontology/DCM"; + + public String getNameForSystem(String system) { + final int lastPipe = system.lastIndexOf('|'); + final String systemBaseName = lastPipe == -1 ? system : system.substring(0,lastPipe); + final String systemVersion = lastPipe == -1 ? null : system.substring(lastPipe + 1); + + if (systemBaseName.equals(SNOMED_SCT_CODESYSTEM_URL)) + return getVersionedSystem("snomed", systemVersion); + if (systemBaseName.equals(RXNORM_CODESYSTEM_URL)) + return getVersionedSystem("rxnorm", systemVersion); + if (systemBaseName.equals(LOINC_CODESYSTEM_URL)) + return getVersionedSystem("loinc", systemVersion); + if (systemBaseName.equals(UCUM_CODESYSTEM_URL)) + return getVersionedSystem("ucum", systemVersion); + if (systemBaseName.startsWith(HL7_SID_CODESYSTEM_BASE_URL)) + return getVersionedSystem(normalizeBaseURL(HL7_SID_CODESYSTEM_BASE_URL, systemBaseName), systemVersion); + if (systemBaseName.equals(_11073_CODESYSTEM_URN)) + return getVersionedSystem("11073", systemVersion); + if (systemBaseName.startsWith(ISO_CODESYSTEM_URN)) + return getVersionedSystem("iso"+systemBaseName.substring(ISO_CODESYSTEM_URN.length()).replace(":", ""), systemVersion); + if (systemBaseName.startsWith(HL7_TERMINOLOGY_CODESYSTEM_BASE_URL)) + return getVersionedSystem(normalizeBaseURL(HL7_TERMINOLOGY_CODESYSTEM_BASE_URL, systemBaseName), systemVersion); + if (systemBaseName.startsWith(HL7_FHIR_CODESYSTEM_BASE_URL)) + return getVersionedSystem(normalizeBaseURL(HL7_FHIR_CODESYSTEM_BASE_URL, systemBaseName), systemVersion); + if (systemBaseName.equals(LANG_CODESYSTEM_URN)) + return getVersionedSystem("lang", systemVersion); + if (systemBaseName.equals(MIMETYPES_CODESYSTEM_URN)) + return getVersionedSystem("mimetypes", systemVersion); + if (systemBaseName.equals(DICOM_CODESYSTEM_URL)) + return getVersionedSystem("dicom", systemVersion); + return getVersionedSystem(systemBaseName.replace("/", "_").replace(":", "_").replace("?", "X").replace("#", "X"), systemVersion); + } + + public String normalizeBaseURL(String baseUrl, String fullUrl) { + return fullUrl.substring(baseUrl.length()).replace("/", ""); + } + + public String getVersionedSystem(String baseSystem, String version) { + if (version != null) { + return baseSystem + "_" + version; + } + return baseSystem; + } + } + public class CacheToken { private String name; private String key; private String request; public void setName(String n) { + String systemName = getSystemNameKeyGenerator().getNameForSystem(n); if (name == null) - name = n; - else if (!n.equals(name)) + name = systemName; + else if (!systemName.equals(name)) name = NAME_FOR_NO_SYSTEM; } + + public String getName() { + return name; + } } private class CacheEntry { @@ -130,7 +202,7 @@ public class TerminologyCache { public CacheToken generateValidationToken(ValidationOptions options, Coding code, ValueSet vs) { CacheToken ct = new CacheToken(); if (code.hasSystem()) - ct.name = getNameForSystem(code.getSystem()); + ct.name = getSystemNameKeyGenerator().getNameForSystem(code.getSystem()); else ct.name = NAME_FOR_NO_SYSTEM; JsonParser json = new JsonParser(); @@ -159,7 +231,7 @@ public class TerminologyCache { CacheToken ct = new CacheToken(); for (Coding c : code.getCoding()) { if (c.hasSystem()) - ct.setName(getNameForSystem(c.getSystem())); + ct.setName(c.getSystem()); } JsonParser json = new JsonParser(); json.setOutputStyle(OutputStyle.PRETTY); @@ -190,13 +262,13 @@ public class TerminologyCache { ValueSet vsc = getVSEssense(vs); for (ConceptSetComponent inc : vs.getCompose().getInclude()) if (inc.hasSystem()) - ct.setName(getNameForSystem(inc.getSystem())); + ct.setName(inc.getSystem()); for (ConceptSetComponent inc : vs.getCompose().getExclude()) if (inc.hasSystem()) - ct.setName(getNameForSystem(inc.getSystem())); + ct.setName(inc.getSystem()); for (ValueSetExpansionContainsComponent inc : vs.getExpansion().getContains()) if (inc.hasSystem()) - ct.setName(getNameForSystem(inc.getSystem())); + ct.setName(inc.getSystem()); JsonParser json = new JsonParser(); json.setOutputStyle(OutputStyle.PRETTY); try { @@ -208,33 +280,7 @@ public class TerminologyCache { return ct; } - private String getNameForSystem(String system) { - if (system.equals("http://snomed.info/sct")) - return "snomed"; - if (system.equals("http://www.nlm.nih.gov/research/umls/rxnorm")) - return "rxnorm"; - if (system.equals("http://loinc.org")) - return "loinc"; - if (system.equals("http://unitsofmeasure.org")) - return "ucum"; - if (system.startsWith("http://hl7.org/fhir/sid/")) - return system.substring(24).replace("/", ""); - if (system.startsWith("urn:iso:std:iso:")) - return "iso"+system.substring(16).replace(":", ""); - if (system.startsWith("http://terminology.hl7.org/CodeSystem/")) - return system.substring(38).replace("/", ""); - if (system.startsWith("http://hl7.org/fhir/")) - return system.substring(20).replace("/", ""); - if (system.equals("urn:ietf:bcp:47")) - return "lang"; - if (system.equals("urn:ietf:bcp:13")) - return "mimetypes"; - if (system.equals("urn:iso:std:iso:11073:10101")) - return "11073"; - if (system.equals("http://dicom.nema.org/resources/ontology/DCM")) - return "dicom"; - return system.replace("/", "_").replace(":", "_").replace("?", "X").replace("#", "X"); - } + public NamedCache getNamedCache(CacheToken cacheToken) { NamedCache nc = caches.get(cacheToken.name); @@ -520,7 +566,7 @@ public class TerminologyCache { public void removeCS(String url) { synchronized (lock) { - String name = getNameForSystem(url); + String name = getSystemNameKeyGenerator().getNameForSystem(url); if (caches.containsKey(name)) { caches.remove(name); } diff --git a/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/context/CacheTestUtils.java b/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/context/CacheTestUtils.java new file mode 100644 index 000000000..2fcd71fc7 --- /dev/null +++ b/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/context/CacheTestUtils.java @@ -0,0 +1,8 @@ +package org.hl7.fhir.r4b.context; + +import org.hl7.fhir.utilities.validation.ValidationOptions; + +public class CacheTestUtils { + public static final ValidationOptions validationOptions = new ValidationOptions().guessSystem().setVersionFlexible(false); + +} diff --git a/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/context/TerminologyCacheTests.java b/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/context/TerminologyCacheTests.java new file mode 100644 index 000000000..1a5222230 --- /dev/null +++ b/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/context/TerminologyCacheTests.java @@ -0,0 +1,68 @@ +package org.hl7.fhir.r4b.context; + + +import org.hl7.fhir.r4b.model.Coding; +import org.hl7.fhir.r4b.model.ValueSet; +import org.hl7.fhir.utilities.tests.ResourceLoaderTests; + +import org.junit.jupiter.params.ParameterizedTest; + +import org.junit.jupiter.params.provider.CsvSource; + +import java.io.IOException; + +import java.net.URISyntaxException; + + +import static org.junit.jupiter.api.Assertions.assertEquals; + + +public class TerminologyCacheTests implements ResourceLoaderTests { + + + + private TerminologyCache createTerminologyCache() throws IOException { + Object lock = new Object(); + TerminologyCache terminologyCache = new TerminologyCache(lock, null); + return terminologyCache; + } + + + + @ParameterizedTest + @CsvSource({ + "http://terminology.hl7.org/CodeSystem/id,id", + "http://hl7.org/fhir/id,id", + "http://hl7.org/fhir/sid/id,id", + "http://www.nlm.nih.gov/research/umls/rxnorm,rxnorm", + "http://snomed.info/sct,snomed", + "http://www.nlm.nih.gov/research/umls/rxnorm,rxnorm", + "http://loinc.org,loinc", + "http://unitsofmeasure.org,ucum", + "urn:iso:std:iso:id,isoid", + "urn:ietf:bcp:47,lang", + "urn:ietf:bcp:13,mimetypes", + "urn:iso:std:iso:11073:10101,11073", + "my://random/system?with#chars,my___random_systemXwithXchars", + "http://dicom.nema.org/resources/ontology/DCM,dicom" + }) + public void testCacheTokenGeneration(String system, String expectedName) throws IOException, URISyntaxException { + + TerminologyCache terminologyCache = createTerminologyCache(); + ValueSet valueSet = new ValueSet(); + { + Coding coding = new Coding(); + coding.setSystem(system); + TerminologyCache.CacheToken cacheToken = terminologyCache.generateValidationToken(CacheTestUtils.validationOptions, + coding, valueSet); + assertEquals(expectedName, cacheToken.getName()); + } + { + Coding coding = new Coding(); + coding.setSystem(system + "|dummyVersion"); + TerminologyCache.CacheToken cacheToken = terminologyCache.generateValidationToken(CacheTestUtils.validationOptions, + coding, valueSet); + assertEquals(expectedName + "_dummyVersion", cacheToken.getName()); + } + } +}