From 4b173cbfb3643162c11d2e71e0f7c876eb3cc7ac Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 12 Nov 2019 19:40:42 +1100 Subject: [PATCH] check in new resource manager --- .../fhir/r5/context/BaseWorkerContext.java | 303 ++++++++++-------- .../r5/context/MetadataResourceManager.java | 180 +++++++++++ .../fhir/r5/context/SimpleWorkerContext.java | 2 +- .../java/org/hl7/fhir/r5/test/AllR5Tests.java | 1 + .../test/MetadataResourceManagerTester.java | 226 +++++++++++++ .../hl7/fhir/utilities/VersionUtilities.java | 20 +- 6 files changed, 593 insertions(+), 139 deletions(-) create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/MetadataResourceManager.java create mode 100644 org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/MetadataResourceManagerTester.java diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java index 634dd053d..61679e4f8 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java @@ -23,6 +23,8 @@ package org.hl7.fhir.r5.context; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -36,6 +38,7 @@ import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.TerminologyServiceException; import org.hl7.fhir.r5.conformance.ProfileUtilities; +import org.hl7.fhir.r5.context.BaseWorkerContext.MetadataResourceVersionComparator; import org.hl7.fhir.r5.context.IWorkerContext.ILoggingService.LogCategory; import org.hl7.fhir.r5.context.TerminologyCache.CacheToken; import org.hl7.fhir.r5.model.BooleanType; @@ -79,6 +82,7 @@ import org.hl7.fhir.utilities.OIDUtils; import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; @@ -86,22 +90,52 @@ import com.google.gson.JsonObject; public abstract class BaseWorkerContext implements IWorkerContext { + public class MetadataResourceVersionComparator implements Comparator { + + private List list; + + public MetadataResourceVersionComparator(List list) { + this.list = list; + } + + @Override + public int compare(T arg1, T arg2) { + String v1 = arg1.getVersion(); + String v2 = arg2.getVersion(); + if (v1 == null && v2 == null) { + return Integer.compare(list.indexOf(arg1), list.indexOf(arg2)); // retain original order + } else if (v1 == null) { + return -1; + } else if (v2 == null) { + return 1; + } else { + String mm1 = VersionUtilities.getMajMin(v1); + String mm2 = VersionUtilities.getMajMin(v2); + if (mm1 == null || mm2 == null) { + return v1.compareTo(v2); + } else { + return mm1.compareTo(mm2); + } + } + } + } + private Object lock = new Object(); // used as a lock for the data that follows private Map> allResourcesById = new HashMap>(); // all maps are to the full URI - private Map codeSystems = new HashMap(); + private MetadataResourceManager codeSystems = new MetadataResourceManager(); private Set supportedCodeSystems = new HashSet(); - private Map valueSets = new HashMap(); - private Map maps = new HashMap(); - protected Map transforms = new HashMap(); - private Map structures = new HashMap(); - private Map guides = new HashMap(); - private Map capstmts = new HashMap(); - private Map searchParameters = new HashMap(); - private Map questionnaires = new HashMap(); - private Map operations = new HashMap(); - private Map plans = new HashMap(); + private MetadataResourceManager valueSets = new MetadataResourceManager(); + private MetadataResourceManager maps = new MetadataResourceManager(); + protected MetadataResourceManager transforms = new MetadataResourceManager(); + private MetadataResourceManager structures = new MetadataResourceManager(); + private MetadataResourceManager guides = new MetadataResourceManager(); + private MetadataResourceManager capstmts = new MetadataResourceManager(); + private MetadataResourceManager searchParameters = new MetadataResourceManager(); + private MetadataResourceManager questionnaires = new MetadataResourceManager(); + private MetadataResourceManager operations = new MetadataResourceManager(); + private MetadataResourceManager plans = new MetadataResourceManager(); private List systems = new ArrayList(); private UcumService ucumService; @@ -128,7 +162,8 @@ public abstract class BaseWorkerContext implements IWorkerContext { txCache = new TerminologyCache(lock, null); } - public BaseWorkerContext(Map codeSystems, Map valueSets, Map maps, Map profiles, Map guides) throws FileNotFoundException, IOException, FHIRException { + public BaseWorkerContext(MetadataResourceManager codeSystems, MetadataResourceManager valueSets, MetadataResourceManager maps, MetadataResourceManager profiles, + MetadataResourceManager guides) throws FileNotFoundException, IOException, FHIRException { super(); this.codeSystems = codeSystems; this.valueSets = valueSets; @@ -142,19 +177,19 @@ public abstract class BaseWorkerContext implements IWorkerContext { synchronized (other.lock) { // tricky, because you need to lock this as well, but it's really not in use yet allResourcesById.putAll(other.allResourcesById); translator = other.translator; - codeSystems.putAll(other.codeSystems); + codeSystems.copy(other.codeSystems); txcaps = other.txcaps; - valueSets.putAll(other.valueSets); - maps.putAll(other.maps); - transforms.putAll(other.transforms); - structures.putAll(other.structures); - searchParameters.putAll(other.searchParameters); - plans.putAll(other.plans); - questionnaires.putAll(other.questionnaires); - operations.putAll(other.operations); + valueSets.copy(other.valueSets); + maps.copy(other.maps); + transforms.copy(other.transforms); + structures.copy(other.structures); + searchParameters.copy(other.searchParameters); + plans.copy(other.plans); + questionnaires.copy(other.questionnaires); + operations.copy(other.operations); systems.addAll(other.systems); - guides.putAll(other.guides); - capstmts.putAll(other.capstmts); + guides.copy(other.guides); + capstmts.copy(other.capstmts); allowLoadingDuplicates = other.allowLoadingDuplicates; tsServer = other.tsServer; @@ -187,27 +222,27 @@ public abstract class BaseWorkerContext implements IWorkerContext { if (!allowLoadingDuplicates && hasResource(r.getClass(), url)) throw new DefinitionException("Duplicate Resource " + url); if (r instanceof StructureDefinition) - seeMetadataResource((StructureDefinition) m, structures, false); + structures.see((StructureDefinition) m); else if (r instanceof ValueSet) - seeMetadataResource((ValueSet) m, valueSets, false); + valueSets.see((ValueSet) m); else if (r instanceof CodeSystem) - seeMetadataResource((CodeSystem) m, codeSystems, false); + codeSystems.see((CodeSystem) m); else if (r instanceof ImplementationGuide) - seeMetadataResource((ImplementationGuide) m, guides, false); + guides.see((ImplementationGuide) m); else if (r instanceof CapabilityStatement) - seeMetadataResource((CapabilityStatement) m, capstmts, false); + capstmts.see((CapabilityStatement) m); else if (r instanceof SearchParameter) - seeMetadataResource((SearchParameter) m, searchParameters, false); + searchParameters.see((SearchParameter) m); else if (r instanceof PlanDefinition) - seeMetadataResource((PlanDefinition) m, plans, false); + plans.see((PlanDefinition) m); else if (r instanceof OperationDefinition) - seeMetadataResource((OperationDefinition) m, operations, false); + operations.see((OperationDefinition) m); else if (r instanceof Questionnaire) - seeMetadataResource((Questionnaire) m, questionnaires, true); + questionnaires.see((Questionnaire) m); else if (r instanceof ConceptMap) - seeMetadataResource((ConceptMap) m, maps, false); + maps.see((ConceptMap) m); else if (r instanceof StructureMap) - seeMetadataResource((StructureMap) m, transforms, false); + transforms.see((StructureMap) m); else if (r instanceof NamingSystem) systems.add((NamingSystem) r); } @@ -261,26 +296,38 @@ public abstract class BaseWorkerContext implements IWorkerContext { throw new Error("Delimited versions have exact match for delimiter '"+delimiter+"' : "+newParts+" vs "+oldParts); } - protected void seeMetadataResource(T r, Map map, boolean addId) throws FHIRException { - if (addId) - map.put(r.getId(), r); // todo: why? - if (!map.containsKey(r.getUrl())) - map.put(r.getUrl(), r); - else { - // If this resource already exists, see if it's the newest business version. The default resource to return if not qualified is the most recent business version - MetadataResource existingResource = (MetadataResource)map.get(r.getUrl()); - if (r.hasVersion() && existingResource.hasVersion() && !r.getVersion().equals(existingResource.getVersion())) { - if (laterVersion(r.getVersion(), existingResource.getVersion())) { - map.remove(r.getUrl()); - map.put(r.getUrl(), r); - } - } else - map.remove(r.getUrl()); + protected void seeMetadataResource(T r, Map map, List list, boolean addId) throws FHIRException { +// if (addId) + // map.put(r.getId(), r); // todo: why? + list.add(r); + if (r.hasUrl()) { + // first, this is the correct reosurce for this version (if it has a version) + if (r.hasVersion()) { + map.put(r.getUrl()+"|"+r.getVersion(), r); + } + // if we haven't get anything for this url, it's the correct version + if (!map.containsKey(r.getUrl())) map.put(r.getUrl(), r); -// throw new FHIRException("Multiple declarations of resource with same canonical URL (" + r.getUrl() + ") and version (" + (r.hasVersion() ? r.getVersion() : "" ) + ")"); + else { + List rl = new ArrayList(); + for (T t : list) { + if (t.getUrl().equals(r.getUrl()) && !rl.contains(t)) { + rl.add(t); + } + } + Collections.sort(rl, new MetadataResourceVersionComparator(list)); + map.put(r.getUrl(), rl.get(rl.size()-1)); + T latest = null; + for (T t : rl) { + if (VersionUtilities.versionsCompatible(t.getVersion(), r.getVersion())) { + latest = t; + } + } + if (latest != null) { // might be null if it's not using semver + map.put(r.getUrl()+"|"+VersionUtilities.getMajMin(latest.getVersion()), rl.get(rl.size()-1)); + } + } } - if (r.hasVersion()) - map.put(r.getUrl()+"|"+r.getVersion(), r); } @Override @@ -293,7 +340,7 @@ public abstract class BaseWorkerContext implements IWorkerContext { @Override public boolean supportsSystem(String system) throws TerminologyServiceException { synchronized (lock) { - if (codeSystems.containsKey(system) && codeSystems.get(system).getContent() != CodeSystemContentMode.NOTPRESENT) + if (codeSystems.has(system) && codeSystems.get(system).getContent() != CodeSystemContentMode.NOTPRESENT) return true; else if (supportedCodeSystems.contains(system)) return true; @@ -635,7 +682,7 @@ public abstract class BaseWorkerContext implements IWorkerContext { public List findMapsForSource(String url) throws FHIRException { synchronized (lock) { List res = new ArrayList(); - for (ConceptMap map : maps.values()) + for (ConceptMap map : maps.getList()) if (((Reference) map.getSource()).getReference().equals(url)) res.add(map); return res; @@ -706,27 +753,27 @@ public abstract class BaseWorkerContext implements IWorkerContext { if (uri.contains("#")) uri = uri.substring(0, uri.indexOf("#")); if (class_ == Resource.class || class_ == null) { - if (structures.containsKey(uri)) + if (structures.has(uri)) return (T) structures.get(uri); - if (guides.containsKey(uri)) + if (guides.has(uri)) return (T) guides.get(uri); - if (capstmts.containsKey(uri)) + if (capstmts.has(uri)) return (T) capstmts.get(uri); - if (valueSets.containsKey(uri)) + if (valueSets.has(uri)) return (T) valueSets.get(uri); - if (codeSystems.containsKey(uri)) + if (codeSystems.has(uri)) return (T) codeSystems.get(uri); - if (operations.containsKey(uri)) + if (operations.has(uri)) return (T) operations.get(uri); - if (searchParameters.containsKey(uri)) + if (searchParameters.has(uri)) return (T) searchParameters.get(uri); - if (plans.containsKey(uri)) + if (plans.has(uri)) return (T) plans.get(uri); - if (maps.containsKey(uri)) + if (maps.has(uri)) return (T) maps.get(uri); - if (transforms.containsKey(uri)) + if (transforms.has(uri)) return (T) transforms.get(uri); - if (questionnaires.containsKey(uri)) + if (questionnaires.has(uri)) return (T) questionnaires.get(uri); for (Map rt : allResourcesById.values()) { for (Resource r : rt.values()) { @@ -747,13 +794,13 @@ public abstract class BaseWorkerContext implements IWorkerContext { } else if (class_ == StructureMap.class) { return (T) transforms.get(uri); } else if (class_ == ValueSet.class) { - if (valueSets.containsKey(uri+"|"+version)) - return (T) valueSets.get(uri+"|"+version); + if (valueSets.has(uri, version)) + return (T) valueSets.get(uri, version); else return (T) valueSets.get(uri); } else if (class_ == CodeSystem.class) { - if (codeSystems.containsKey(uri+"|"+version)) - return (T) codeSystems.get(uri+"|"+version); + if (codeSystems.has(uri, version)) + return (T) codeSystems.get(uri, version); else return (T) codeSystems.get(uri); } else if (class_ == ConceptMap.class) { @@ -765,17 +812,10 @@ public abstract class BaseWorkerContext implements IWorkerContext { return (T) od; } else if (class_ == SearchParameter.class) { SearchParameter res = searchParameters.get(uri); - if (res == null) { - StringBuilder b = new StringBuilder(); - for (String s : searchParameters.keySet()) { - b.append(s); - b.append("\r\n"); - } - } return (T) res; } } - if (class_ == CodeSystem.class && codeSystems.containsKey(uri)) + if (class_ == CodeSystem.class && codeSystems.has(uri)) return (T) codeSystems.get(uri); if (class_ == Questionnaire.class) @@ -943,23 +983,23 @@ public abstract class BaseWorkerContext implements IWorkerContext { map.remove(id); if (fhirType.equals("StructureDefinition")) - dropMetadataResource(structures, id); + structures.drop(id); else if (fhirType.equals("ImplementationGuide")) - dropMetadataResource(guides, id); + guides.drop(id); else if (fhirType.equals("CapabilityStatement")) - dropMetadataResource(capstmts, id); + capstmts.drop(id); else if (fhirType.equals("ValueSet")) - dropMetadataResource(valueSets, id); + valueSets.drop(id); else if (fhirType.equals("CodeSystem")) - dropMetadataResource(codeSystems, id); + codeSystems.drop(id); else if (fhirType.equals("OperationDefinition")) - dropMetadataResource(operations, id); + operations.drop(id); else if (fhirType.equals("Questionnaire")) - dropMetadataResource(questionnaires, id); + questionnaires.drop(id); else if (fhirType.equals("ConceptMap")) - dropMetadataResource(maps, id); + maps.drop(id); else if (fhirType.equals("StructureMap")) - dropMetadataResource(transforms, id); + transforms.drop(id); else if (fhirType.equals("NamingSystem")) for (int i = systems.size()-1; i >= 0; i--) { if (systems.get(i).getId().equals(id)) @@ -984,15 +1024,15 @@ public abstract class BaseWorkerContext implements IWorkerContext { public List allConformanceResources() { synchronized (lock) { List result = new ArrayList(); - result.addAll(structures.values()); - result.addAll(guides.values()); - result.addAll(capstmts.values()); - result.addAll(codeSystems.values()); - result.addAll(valueSets.values()); - result.addAll(maps.values()); - result.addAll(transforms.values()); - result.addAll(plans.values()); - result.addAll(questionnaires.values()); + structures.listAllM(result); + guides.listAllM(result); + capstmts.listAllM(result); + codeSystems.listAllM(result); + valueSets.listAllM(result); + maps.listAllM(result); + transforms.listAllM(result); + plans.listAllM(result); + questionnaires.listAllM(result); return result; } } @@ -1016,7 +1056,7 @@ public abstract class BaseWorkerContext implements IWorkerContext { public List listMaps() { List m = new ArrayList(); synchronized (lock) { - m.addAll(maps.values()); + maps.listAll(m); } return m; } @@ -1024,7 +1064,7 @@ public abstract class BaseWorkerContext implements IWorkerContext { public List listTransforms() { List m = new ArrayList(); synchronized (lock) { - m.addAll(transforms.values()); + transforms.listAll(m); } return m; } @@ -1038,7 +1078,7 @@ public abstract class BaseWorkerContext implements IWorkerContext { public List listStructures() { List m = new ArrayList(); synchronized (lock) { - m.addAll(structures.values()); + structures.listAll(m); } return m; } @@ -1138,55 +1178,44 @@ public abstract class BaseWorkerContext implements IWorkerContext { public List getStructures() { List res = new ArrayList<>(); synchronized (lock) { // tricky, because you need to lock this as well, but it's really not in use yet - res.addAll(structures.values()); + structures.listAll(res); } return res; } public String getLinkForUrl(String corePath, String url) { - for (CodeSystem r : codeSystems.values()) - if (url.equals(r.getUrl())) - return r.getUserString("path"); + if (codeSystems.has(url)) + return codeSystems.get(url).getUserString("path"); - for (ValueSet r : valueSets.values()) - if (url.equals(r.getUrl())) - return r.getUserString("path"); + if (valueSets.has(url)) + return valueSets.get(url).getUserString("path"); + + if (maps.has(url)) + return maps.get(url).getUserString("path"); - for (ConceptMap r : maps.values()) - if (url.equals(r.getUrl())) - return r.getUserString("path"); + if (transforms.has(url)) + return transforms.get(url).getUserString("path"); - for (StructureMap r : transforms.values()) - if (url.equals(r.getUrl())) - return r.getUserString("path"); + if (structures.has(url)) + return structures.get(url).getUserString("path"); - for (StructureDefinition r : structures.values()) - if (url.equals(r.getUrl())) - return r.getUserString("path"); + if (guides.has(url)) + return guides.get(url).getUserString("path"); - for (ImplementationGuide r : guides.values()) - if (url.equals(r.getUrl())) - return r.getUserString("path"); + if (capstmts.has(url)) + return capstmts.get(url).getUserString("path"); + + if (searchParameters.has(url)) + return searchParameters.get(url).getUserString("path"); + + if (questionnaires.has(url)) + return questionnaires.get(url).getUserString("path"); + + if (operations.has(url)) + return operations.get(url).getUserString("path"); - for (CapabilityStatement r : capstmts.values()) - if (url.equals(r.getUrl())) - return r.getUserString("path"); - - for (SearchParameter r : searchParameters.values()) - if (url.equals(r.getUrl())) - return r.getUserString("path"); - - for (Questionnaire r : questionnaires.values()) - if (url.equals(r.getUrl())) - return r.getUserString("path"); - - for (OperationDefinition r : operations.values()) - if (url.equals(r.getUrl())) - return r.getUserString("path"); - - for (PlanDefinition r : plans.values()) - if (url.equals(r.getUrl())) - return r.getUserString("path"); + if (plans.has(url)) + return plans.get(url).getUserString("path"); if (url.equals("http://loinc.org")) return corePath+"loinc.html"; @@ -1201,7 +1230,7 @@ public abstract class BaseWorkerContext implements IWorkerContext { public List allImplementationGuides() { List res = new ArrayList<>(); - res.addAll(guides.values()); + guides.listAll(res); return res; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/MetadataResourceManager.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/MetadataResourceManager.java new file mode 100644 index 000000000..75a04ea07 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/MetadataResourceManager.java @@ -0,0 +1,180 @@ +package org.hl7.fhir.r5.context; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import org.hl7.fhir.r5.context.BaseWorkerContext.MetadataResourceVersionComparator; +import org.hl7.fhir.r5.model.CodeSystem; +import org.hl7.fhir.r5.model.MetadataResource; +import org.hl7.fhir.utilities.VersionUtilities; + +/** + * This manages a cached list of resources, and provides high speed access by URL / URL+version, and assumes that patch version doesn't matter for access + * note, though, that not all resources have semver versions + * + * @author graha + * + */ + +public class MetadataResourceManager { + + public class MetadataResourceVersionComparator implements Comparator { + @Override + public int compare(T arg1, T arg2) { + String v1 = arg1.getVersion(); + String v2 = arg2.getVersion(); + if (v1 == null && v2 == null) { + return Integer.compare(list.indexOf(arg1), list.indexOf(arg2)); // retain original order + } else if (v1 == null) { + return -1; + } else if (v2 == null) { + return 1; + } else { + String mm1 = VersionUtilities.getMajMin(v1); + String mm2 = VersionUtilities.getMajMin(v2); + if (mm1 == null || mm2 == null) { + return v1.compareTo(v2); + } else { + return mm1.compareTo(mm2); + } + } + } + } + + + private List list = new ArrayList<>(); + private Map map = new HashMap<>(); + + public void copy(MetadataResourceManager source) { + list.clear(); + map.clear(); + list.addAll(source.list); + map.putAll(source.map); + } + + public void see(T r) { + if (!r.hasId()) { + r.setId(UUID.randomUUID().toString()); + } + if (map.containsKey(r.getId())) { + drop(r.getId()); + } + list.add(r); + map.put(r.getId(), r); // we do this so we can drop by id + + if (r.hasUrl()) { + // first, this is the correct reosurce for this version (if it has a version) + if (r.hasVersion()) { + map.put(r.getUrl()+"|"+r.getVersion(), r); + } + updateList(r.getUrl(), r.getVersion()); + } + } + + private void updateList(String url, String version) { + List rl = new ArrayList(); + for (T t : list) { + if (url.equals(t.getUrl()) && !rl.contains(t)) { + rl.add(t); + } + } + if (rl.size() > 0) { + // sort by version as much as we are able + Collections.sort(rl, new MetadataResourceVersionComparator()); + // the current is the latest + map.put(url, rl.get(rl.size()-1)); + // now, also, the latest for major/minor + if (version != null) { + T latest = null; + for (T t : rl) { + if (VersionUtilities.versionsCompatible(t.getVersion(), version)) { + latest = t; + } + } + if (latest != null) { // might be null if it's not using semver + map.put(url+"|"+VersionUtilities.getMajMin(latest.getVersion()), rl.get(rl.size()-1)); + } + } + } + } + + + public T get(String url) { + return map.get(url); + } + + public boolean has(String url) { + return map.containsKey(url); + } + + public T get(String system, String version) { + if (map.containsKey(system+"|"+version)) + return map.get(system+"|"+version); + String mm = VersionUtilities.getMajMin(version); + if (mm != null) + return map.get(system+"|"+mm); + else + return null; + } + + public boolean has(String system, String version) { + if (map.containsKey(system+"|"+version)) + return true; + String mm = VersionUtilities.getMajMin(version); + if (mm != null) + return map.containsKey(system+"|"+mm); + else + return false; + } + + public int size() { + return list.size(); + } + + public void drop(String id) { + T res = map.get(id); + if (res != null) { + list.remove(res); + map.remove(id); + map.remove(res.getUrl()); + if (res.hasVersion()) { + map.remove(res.getUrl()+"|"+res.getVersion()); + String mm = VersionUtilities.getMajMin(res.getVersion()); + if (mm != null) { + map.remove(res.getUrl()+"|"+mm); + } + } + updateList(res.getUrl(), res.getVersion()); + } + } + + + public void listAll(List result) { + result.addAll(list); + } + + public void listAllM(List result) { + result.addAll(list); + } + + public void clear() { + list.clear(); + map.clear(); + + } + + public List getList() { + return list; + } + + public Set keys() { + return map.keySet(); + } + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java index db9234ab9..759c5e9e6 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java @@ -608,7 +608,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon } public String listMapUrls() { - return Utilities.listCanonicalUrls(transforms.keySet()); + return Utilities.listCanonicalUrls(transforms.keys()); } diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/AllR5Tests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/AllR5Tests.java index b4902540c..70ff058f8 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/AllR5Tests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/AllR5Tests.java @@ -19,6 +19,7 @@ import org.junit.runners.Suite.SuiteClasses; ShexGeneratorTests.class, BaseDateTimeTypeTest.class, OpenApiGeneratorTest.class, + MetadataResourceManagerTester.class, SnapShotGenerationTests.class}) public class AllR5Tests { diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/MetadataResourceManagerTester.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/MetadataResourceManagerTester.java new file mode 100644 index 000000000..c9ff93b1a --- /dev/null +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/MetadataResourceManagerTester.java @@ -0,0 +1,226 @@ +package org.hl7.fhir.r5.test; + +import static org.junit.Assert.*; + +import org.hl7.fhir.r5.context.MetadataResourceManager; +import org.hl7.fhir.r5.model.CodeSystem; +import org.hl7.fhir.r5.model.ValueSet; +import org.junit.Assert; +import org.junit.Test; + +public class MetadataResourceManagerTester { + + private MetadataResourceManager mrm = new MetadataResourceManager<>(); + + @Test + public void testSingleNoVersion() { + ValueSet vs = new ValueSet(); + vs.setId("2345"); + vs.setUrl("http://url/ValueSet/234"); + // no version + + mrm.clear(); + mrm.see(vs); + + Assert.assertEquals(mrm.size(), 1); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234")); + Assert.assertNotNull(mrm.get("2345")); + Assert.assertNull(mrm.get("http://url/ValueSet/234", "4.0.0")); + mrm.see(vs); + Assert.assertEquals(mrm.size(), 1); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234")); + Assert.assertNotNull(mrm.get("2345")); + Assert.assertNull(mrm.get("http://url/ValueSet/234", "4.0.0")); + + mrm.drop("2344"); + Assert.assertEquals(mrm.size(), 1); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234")); + Assert.assertNotNull(mrm.get("2345")); + Assert.assertNull(mrm.get("http://url/ValueSet/234", "4.0.0")); + + mrm.drop("2345"); + Assert.assertEquals(mrm.size(), 0); + Assert.assertNull(mrm.get("http://url/ValueSet/234")); + Assert.assertNull(mrm.get("2345")); + Assert.assertNull(mrm.get("http://url/ValueSet/234", "4.0.0")); + } + + @Test + public void testSingleWithVersion() { + ValueSet vs = new ValueSet(); + vs.setId("2345"); + vs.setUrl("http://url/ValueSet/234"); + vs.setVersion("4.0.1"); + + mrm.clear(); + mrm.see(vs); + + Assert.assertNotNull(mrm.get("2345")); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234")); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.0")); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.1")); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.2")); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0")); + Assert.assertNull(mrm.get("http://url/ValueSet/234", "4.1")); + } + + @Test + public void testSingleWithVersionNotSemVer() { + ValueSet vs = new ValueSet(); + vs.setId("2345"); + vs.setUrl("http://url/ValueSet/234"); + vs.setVersion("20140403"); + + mrm.clear(); + mrm.see(vs); + + Assert.assertNotNull(mrm.get("2345")); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234")); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "20140403")); + Assert.assertNull(mrm.get("http://url/ValueSet/234", "20140402")); + Assert.assertNull(mrm.get("http://url/ValueSet/234", "2014")); + } + + @Test + public void testSingleWithVersions1() { + ValueSet vs1 = new ValueSet(); + vs1.setId("2345"); + vs1.setUrl("http://url/ValueSet/234"); + vs1.setVersion("4.0.1"); + vs1.setName("1"); + + ValueSet vs2 = new ValueSet(); + vs2.setId("2346"); + vs2.setUrl("http://url/ValueSet/234"); + vs2.setVersion("4.0.2"); + vs2.setName("2"); + + mrm.clear(); + mrm.see(vs1); + + Assert.assertEquals(mrm.size(), 1); + Assert.assertNotNull(mrm.get("2345")); + Assert.assertEquals(mrm.get("2345").getName(), "1"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234").getName(), "1"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.0")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0.0").getName(), "1"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.1")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0.1").getName(), "1"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.2")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0.2").getName(), "1"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0").getName(), "1"); + Assert.assertNull(mrm.get("http://url/ValueSet/234", "4.1")); + + mrm.see(vs2); + + Assert.assertEquals(mrm.size(), 2); + Assert.assertNotNull(mrm.get("2345")); + Assert.assertEquals(mrm.get("2345").getName(), "1"); + Assert.assertNotNull(mrm.get("2346")); + Assert.assertEquals(mrm.get("2346").getName(), "2"); + + Assert.assertNotNull(mrm.get("http://url/ValueSet/234")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234").getName(), "2"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.0")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0.0").getName(), "2"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.1")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0.1").getName(), "1"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.2")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0.2").getName(), "2"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0").getName(), "2"); + Assert.assertNull(mrm.get("http://url/ValueSet/234", "4.1")); + + mrm.drop("2346"); // vs2; + Assert.assertEquals(mrm.size(), 1); + Assert.assertNotNull(mrm.get("2345")); + Assert.assertNull(mrm.get("2346")); + Assert.assertEquals(mrm.get("2345").getName(), "1"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234").getName(), "1"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.0")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0.0").getName(), "1"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.1")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0.1").getName(), "1"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.2")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0.2").getName(), "1"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0").getName(), "1"); + Assert.assertNull(mrm.get("http://url/ValueSet/234", "4.1")); + + } + + @Test + public void testSingleWithVersions2() { + ValueSet vs1 = new ValueSet(); + vs1.setId("2345"); + vs1.setUrl("http://url/ValueSet/234"); + vs1.setVersion("4.0.1"); + vs1.setName("1"); + + ValueSet vs2 = new ValueSet(); + vs2.setId("2346"); + vs2.setUrl("http://url/ValueSet/234"); + vs2.setVersion("4.0.2"); + vs2.setName("2"); + + mrm.clear(); + mrm.see(vs1); + + Assert.assertEquals(mrm.size(), 1); + Assert.assertNotNull(mrm.get("2345")); + Assert.assertEquals(mrm.get("2345").getName(), "1"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234").getName(), "1"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.0")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0.0").getName(), "1"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.1")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0.1").getName(), "1"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.2")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0.2").getName(), "1"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0").getName(), "1"); + Assert.assertNull(mrm.get("http://url/ValueSet/234", "4.1")); + + mrm.see(vs2); + + Assert.assertEquals(mrm.size(), 2); + Assert.assertNotNull(mrm.get("2345")); + Assert.assertEquals(mrm.get("2345").getName(), "1"); + Assert.assertNotNull(mrm.get("2346")); + Assert.assertEquals(mrm.get("2346").getName(), "2"); + + Assert.assertNotNull(mrm.get("http://url/ValueSet/234")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234").getName(), "2"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.0")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0.0").getName(), "2"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.1")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0.1").getName(), "1"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.2")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0.2").getName(), "2"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0").getName(), "2"); + Assert.assertNull(mrm.get("http://url/ValueSet/234", "4.1")); + + mrm.drop("2345"); // vs1; + Assert.assertEquals(mrm.size(), 1); + Assert.assertNull(mrm.get("2345")); + Assert.assertNotNull(mrm.get("2346")); + Assert.assertEquals(mrm.get("2346").getName(), "2"); + + Assert.assertNotNull(mrm.get("http://url/ValueSet/234")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234").getName(), "2"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.0")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0.0").getName(), "2"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.1")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0.1").getName(), "2"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0.2")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0.2").getName(), "2"); + Assert.assertNotNull(mrm.get("http://url/ValueSet/234", "4.0")); + Assert.assertEquals(mrm.get("http://url/ValueSet/234", "4.0").getName(), "2"); + Assert.assertNull(mrm.get("http://url/ValueSet/234", "4.1")); + } + +} diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java index 5eb2d8eea..36e6ea2e8 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java @@ -115,7 +115,13 @@ public class VersionUtilities { } public static boolean versionsCompatible(String v1, String v2) { - return v1.substring(0, 3).equals(v2.substring(0, 3)); + String mm1 = getMajMin(v1); + String mm2 = getMajMin(v2); + if (mm1 == null || mm2 == null) { + return false; + } else { + return mm1.equals(mm2); + } } public static boolean isCorePackage(String s) { @@ -125,4 +131,16 @@ public class VersionUtilities { return Utilities.existsInList(s, "hl7.fhir.core","hl7.fhir.r2.core", "hl7.fhir.r2b.core", "hl7.fhir.r3.core", "hl7.fhir.r4.core"); } + public static String getMajMin(String version) { + if (version == null) + return null; + + if (Utilities.charCount(version, '.') == 2) { + String[] p = version.split("\\."); + return p[0]+"."+p[1]; + } else { + return null; + } + } + }