rework OID handling

This commit is contained in:
Grahame Grieve 2023-10-13 10:27:53 +11:00
parent 3d039a89eb
commit 9d742dcd24
10 changed files with 226 additions and 209 deletions

View File

@ -34,6 +34,11 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@ -140,6 +145,7 @@ import org.hl7.fhir.utilities.json.JsonException;
import org.hl7.fhir.utilities.json.model.JsonArray;
import org.hl7.fhir.utilities.json.model.JsonProperty;
import org.hl7.fhir.utilities.json.parser.JsonParser;
import org.hl7.fhir.utilities.npm.NpmPackageIndexBuilder;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
import org.hl7.fhir.utilities.validation.ValidationOptions;
@ -152,9 +158,19 @@ import javax.annotation.Nonnull;
public abstract class BaseWorkerContext extends I18nBase implements IWorkerContext {
class OIDSource {
private String folder;
private Connection db;
protected OIDSource(String folder) {
super();
this.folder = folder;
}
}
private static final boolean QA_CHECK_REFERENCE_SOURCE = false; // see comments below
public class ResourceProxy {
public static class ResourceProxy {
private Resource resource;
private CanonicalResourceProxy proxy;
@ -249,10 +265,8 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
private UcumService ucumService;
protected Map<String, byte[]> binaries = new HashMap<String, byte[]>();
protected Map<String, String> oidCacheCS = new HashMap<>();
protected Map<String, String> oidCacheOth = new HashMap<>();
protected Map<String, String> oidCacheManual = new HashMap<>();
protected List<String> oidFiles = new ArrayList<>();
protected Map<String, Set<String>> oidCacheManual = new HashMap<>();
protected List<OIDSource> oidSources = new ArrayList<>();
protected Map<String, Map<String, ValidationResult>> validationCache = new HashMap<String, Map<String,ValidationResult>>();
protected String name;
@ -334,8 +348,8 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
codeSystemsUsed.addAll(other.codeSystemsUsed);
ucumService = other.ucumService;
binaries.putAll(other.binaries);
oidCacheCS.putAll(other.oidCacheCS);
oidCacheOth.putAll(other.oidCacheOth);
oidSources.addAll(other.oidSources);
oidCacheManual.putAll(other.oidCacheManual);
validationCache.putAll(other.validationCache);
tlogging = other.tlogging;
locator = other.locator;
@ -458,7 +472,6 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
if (r instanceof CodeSystem || r instanceof NamingSystem) {
oidCacheCS.clear();
String url = null;
Set<String> oids = new HashSet<String>();
if (r instanceof CodeSystem) {
@ -485,7 +498,10 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
if (url != null) {
for (String s : oids) {
oidCacheManual.put(s, url);
if (!oidCacheManual.containsKey(s)) {
oidCacheManual.put(s, new HashSet<>());
}
oidCacheManual.get(s).add(url);
}
}
}
@ -2820,52 +2836,56 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
@Override
public String urlForOid(boolean codeSystem, String oid) {
public Set<String> urlsForOid(boolean codeSystem, String oid) {
if (oid == null) {
return null;
}
Map<String, String> cache = codeSystem ?oidCacheCS : oidCacheOth;
if (cache.isEmpty()) {
loadOidCache();
Set<String> urls = new HashSet<>();
if (oidCacheManual.containsKey(oid)) {
urls.addAll(oidCacheManual.get(oid));
}
if (cache.containsKey(oid)) {
return cache.get(oid);
}
switch (oid) {
case "2.16.840.1.113883.6.1" : return "http://loinc.org";
case "2.16.840.1.113883.6.96" : return "http://snomed.info/sct";
default:return null;
}
}
private void loadOidCache() {
oidCacheCS.putAll(oidCacheManual);
for (String ff : oidFiles) {
File f = new File(ff);
if (f.exists()) {
org.hl7.fhir.utilities.json.model.JsonObject oids = null;
for (OIDSource os : oidSources) {
if (os.db == null) {
os.db = connectToOidSource(os.folder);
}
if (os.db != null) {
try {
oids = JsonParser.parseObject(f);
PreparedStatement psql = os.db.prepareStatement("Select URL from OIDMap where OID = ?");
psql.setString(1, oid);
ResultSet rs = psql.executeQuery();
while (rs.next()) {
urls.add(rs.getString(1));
}
} catch (Exception e) {
e.printStackTrace();
}
if (oids != null && oids.has("cs")) {
loadOids(oidCacheCS, oids.getJsonObject("cs"));
}
if (oids != null && oids.has("other")) {
loadOids(oidCacheOth, oids.getJsonObject("other"));
// nothing, there would alreagy have been an error
}
}
}
switch (oid) {
case "2.16.840.1.113883.6.1" :
urls.add("http://loinc.org");
break;
case "2.16.840.1.113883.6.96" :
urls.add("http://snomed.info/sct");
break;
default:
}
return urls;
}
private Connection connectToOidSource(String folder) {
try {
File ff = new File(folder);
File of = new File(Utilities.path(ff.getAbsolutePath(), ".oids.db"));
if (!of.exists()) {
OidIndexBuilder oidBuilder = new OidIndexBuilder(ff, of);
oidBuilder.build();
}
return DriverManager.getConnection("jdbc:sqlite:"+of.getAbsolutePath());
} catch (Exception e) {
return null;
}
}
private void loadOids(Map<String, String> cache, org.hl7.fhir.utilities.json.model.JsonObject oids) {
for (JsonProperty p : oids.getProperties()) {
JsonArray a = (JsonArray) p.getValue();
for (String s : a.asStrings()) {
cache.put(s, p.getName());
}
}
}
}

View File

@ -1034,6 +1034,6 @@ public interface IWorkerContext {
public boolean isForPublication();
public void setForPublication(boolean value);
public String urlForOid(boolean codeSystem, String oid);
public Set<String> urlsForOid(boolean codeSystem, String oid);
}

View File

@ -0,0 +1,116 @@
package org.hl7.fhir.r5.context;
import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashSet;
import java.util.Set;
import org.hl7.fhir.utilities.json.model.JsonObject;
import org.hl7.fhir.utilities.json.parser.JsonParser;
public class OidIndexBuilder {
private File folder;
private File target;
public OidIndexBuilder(File ff, File of) {
super();
this.folder = ff;
this.target = of;
}
public void build() {
System.out.println("Generate OID index for "+folder.getAbsolutePath());
target.delete();
try {
Set<String> matches = new HashSet<String>();
Connection db = DriverManager.getConnection("jdbc:sqlite:"+target.getAbsolutePath());
Statement stmt = db.createStatement();
stmt.execute("CREATE TABLE OIDMap (\r\n"+
"OID nvarchar NOT NULL,\r\n"+
"URL nvarchar NOT NULL,\r\n"+
"PRIMARY KEY (OID, URL))\r\n");
PreparedStatement psql = db.prepareStatement("Insert into OIDMap (OID, URL) values (?, ?)");;
for (File f : folder.listFiles()) {
if (!f.getName().startsWith(".") && f.getName().endsWith(".json")) {
try {
JsonObject json = JsonParser.parseObject(f);
processFile(psql, matches, json);
} catch (Exception e) {
System.out.println("Error processing "+f.getAbsolutePath()+" while generating OIDs: "+e.getMessage());
}
}
}
db.close();
} catch (Exception e) {
System.out.println("Error processing "+folder.getAbsolutePath()+" while generating OIDs: "+e.getMessage());
}
}
private void processFile(PreparedStatement psql, Set<String> matches, JsonObject json) throws SQLException {
String rt = json.asString("resourceType");
if (rt != null) {
Set<String> oids = new HashSet<String>();
String url = null;
if ("NamingSystem".equals(rt)) {
for (JsonObject id : json.getJsonObjects("uniqueId")) {
String t = id.asString("type");
String v = id.asString("value");
if ("url".equals(t) && v != null) {
url = v;
} else if ("oid".equals(t) && v != null) {
oids.add(v);
}
}
if (url != null) {
for (String s : oids) {
addOid(psql, matches, s, url);
}
}
} else {
if (json.hasPrimitive("url")) {
url = json.asString("url");
if (json.has("oid")) {
oids.add(json.asString("oid"));
}
if (json.has("url")) {
String v = json.asString("url");
if (v != null && v.startsWith("urn:oid:")) {
oids.add(v.substring(8));
}
}
for (JsonObject id : json.getJsonObjects("identifier")) {
String v = id.asString("value");
if (v != null && v.startsWith("urn:oid:")) {
oids.add(v.substring(8));
}
}
if (!oids.isEmpty()) {
for (String s : oids) {
addOid(psql, matches, s, url);
}
}
}
}
}
}
private void addOid(PreparedStatement psql, Set<String> matches, String oid, String url) throws SQLException {
String key = oid+"@"+url;
if (!matches.contains(key)) {
matches.add(key);
psql.setString(1, oid);
psql.setString(2, url);
psql.execute();
}
}
}

View File

@ -482,9 +482,9 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
packageTracker.packageLoaded(pi.id(), pi.version());
}
String oi = pi.getFolders().get("package").oidIndexFile();
if (oi != null) {
oidFiles.add(oi);
String of = pi.getFolders().get("package").getFolderPath();
if (of != null) {
oidSources.add(new OIDSource(of));
}
if ((types == null || types.size() == 0) && loader != null) {

View File

@ -17,6 +17,7 @@ import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager.FilesystemPackageCacheMode;
import org.hl7.fhir.utilities.npm.NpmPackage;
import org.hl7.fhir.utilities.npm.ToolsVersion;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
@ -33,19 +34,8 @@ public class CDARoundTripTests {
context = TestingUtilities.getWorkerContext(pcm.loadPackage("hl7.fhir.r4.core", "4.0.1"));
fp = new FHIRPathEngine(context);
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "any.xml"), "any.xml", null);
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "ii.xml"), "ii.xml", null);
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "cd.xml"), "cd.xml", null);
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "ce.xml"), "ce.xml", null);
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "ed.xml"), "ed.xml", null);
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "st.xml"), "st.xml", null);
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "cda.xml"), "cda.xml", null);
for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) {
if (!sd.hasSnapshot()) {
// System.out.println("generate snapshot for " + sd.getUrl());
new ContextUtilities(context).generateSnapshot(sd);
}
}
NpmPackage npm = new FilesystemPackageCacheManager(true).loadPackage("hl7.cda.uv.core");
context.loadFromPackage(npm, null);
}
// old-test
@ -191,16 +181,16 @@ public class CDARoundTripTests {
// }
public void assertsExample(Element cdaExample) {
Assertions.assertEquals("2.16.840.1.113883.3.27.1776", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.templateId.root")));
Assertions.assertEquals("SoEN", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.code.displayName")));
Assertions.assertEquals("SoEN2", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.code.sdtcDisplayName")));
Assertions.assertEquals("c266", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.id.extension")));
Assertions.assertEquals("2.16.840.1.113883.19.4", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.id.root")));
Assertions.assertEquals("X-34133-9", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.code.code")));
Assertions.assertEquals("2.16.840.1.113883.6.1", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.code.codeSystem")));
Assertions.assertEquals("LOINC", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.code.codeSystemName")));
Assertions.assertEquals("Episode Note", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.title.dataString")));
}
Assertions.assertEquals("2.16.840.1.113883.3.27.1776", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.templateId.root")));
Assertions.assertEquals("SoEN", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.code.displayName")));
Assertions.assertEquals("2.16.840.1.113883.1.2.3.4.5.6", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.code.sdtcValueSet")));
Assertions.assertEquals("c266", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.id.extension")));
Assertions.assertEquals("2.16.840.1.113883.19.4", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.id.root")));
Assertions.assertEquals("X-34133-9", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.code.code")));
Assertions.assertEquals("2.16.840.1.113883.6.1", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.code.codeSystem")));
Assertions.assertEquals("LOINC", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.code.codeSystemName")));
Assertions.assertEquals("Episode Note", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.title.dataString")));
}
@Test
/**

View File

@ -1009,6 +1009,7 @@ public class I18nConstants {
public static final String TERMINOLOGY_TX_UNKNOWN_OID = "TERMINOLOGY_TX_UNKNOWN_OID";
public static final String XSI_TYPE_WRONG = "XSI_TYPE_WRONG";
public static final String XSI_TYPE_UNNECESSARY = "XSI_TYPE_UNNECESSARY";
public static final String TERMINOLOGY_TX_OID_MULTIPLE_MATCHES = "TERMINOLOGY_TX_OID_MULTIPLE_MATCHES";
}

View File

@ -207,6 +207,10 @@ public class NpmPackage {
return folderName;
}
public String getFolderPath() {
return folder == null ? null : folder.getAbsolutePath();
}
public boolean readIndex(JsonObject index, Map<String, List<String>> typeMap) {
if (!index.has("index-version") || (index.asInteger("index-version") != NpmPackageIndexBuilder.CURRENT_INDEX_VERSION)) {
return false;
@ -228,13 +232,13 @@ public class NpmPackage {
List<String> res = new ArrayList<>();
if (folder != null) {
for (File f : folder.listFiles()) {
if (!f.isDirectory() && !Utilities.existsInList(f.getName(), "package.json", ".index.json", ".index.db")) {
if (!f.isDirectory() && !Utilities.existsInList(f.getName(), "package.json", ".index.json", ".index.db", ".oids.json", ".oids.db")) {
res.add(f.getName());
}
}
} else {
for (String s : content.keySet()) {
if (!Utilities.existsInList(s, "package.json", ".index.json", ".index.db")) {
if (!Utilities.existsInList(s, "package.json", ".index.json", ".index.db", ".oids.json", ".oids.db")) {
res.add(s);
}
}
@ -324,13 +328,7 @@ public class NpmPackage {
}
}
public String oidIndexFile() throws IOException {
if (folder == null) {
return null;
} else {
return fn(".oids.json");
}
}
}
private String path;
@ -618,35 +616,9 @@ public class NpmPackage {
if (index == null || index.forceArray("files").size() == 0) {
indexFolder(desc, folder);
}
index = folder.oidIndex();
if (index == null || index.forceArray("oids").size() == 0) {
indexOidsInFolder(desc, folder);
}
}
}
public void indexOidsInFolder(String desc, NpmPackageFolder folder) throws FileNotFoundException, IOException {
List<String> remove = new ArrayList<>();
NpmPackageIndexBuilder indexer = new NpmPackageIndexBuilder();
indexer.start(folder.folder != null ? Utilities.path(folder.folder.getAbsolutePath(), ".index.db") : null);
for (String n : folder.listFiles()) {
if (!indexer.seeOidsInFile(n, folder.fetchFile(n))) {
remove.add(n);
}
}
for (String n : remove) {
folder.removeFile(n);
}
String json = JsonParser.compose(indexer.getOidIndex(), true);
try {
if (folder.folder != null) {
TextFile.stringToFile(json, Utilities.path(folder.folder.getAbsolutePath(), ".oids.json"));
}
} catch (Exception e) {
TextFile.stringToFile(json, Utilities.path("[tmp]", ".oids.json"));
throw new IOException("Error parsing "+(desc == null ? "" : desc+"#")+"package/"+folder.folderName+"/.oids.json: "+e.getMessage(), e);
}
}
public void indexFolder(String desc, NpmPackageFolder folder) throws FileNotFoundException, IOException {
List<String> remove = new ArrayList<>();
@ -1182,7 +1154,6 @@ public class NpmPackage {
System.out.println(name+" is null");
} else {
indexer.seeFile(s, b);
indexer.seeOidsInFile(s, b);
if (!s.equals(".index.json") && !s.equals(".index.db") && !s.equals("package.json")) {
TarArchiveEntry entry = new TarArchiveEntry(name);
entry.setSize(b.length);
@ -1198,12 +1169,6 @@ public class NpmPackage {
tar.putArchiveEntry(entry);
tar.write(cnt);
tar.closeArchiveEntry();
cnt = JsonParser.composeBytes(indexer.getOidIndex(), true);
entry = new TarArchiveEntry(n+"/.oids.json");
entry.setSize(cnt.length);
tar.putArchiveEntry(entry);
tar.write(cnt);
tar.closeArchiveEntry();
var file = new File(filename);
if (file.exists()) {
cnt = TextFile.fileToBytes(file);

View File

@ -34,22 +34,12 @@ public class NpmPackageIndexBuilder {
private Connection conn;
private PreparedStatement psql;
private String dbFilename;
private JsonObject oidIndex;
private JsonObject oidIndexCS;
private JsonObject oidIndexOther;
public void start(String filename) {
index = new JsonObject();
index.add("index-version", CURRENT_INDEX_VERSION);
files = new JsonArray();
index.add("files", files);
oidIndex = new JsonObject();
oidIndexCS = new JsonObject();
oidIndex.add("cs", oidIndexCS);
oidIndexOther = new JsonObject();
oidIndex.add("other", oidIndexOther);
dbFilename = filename;
if (filename != null) {
@ -71,7 +61,7 @@ public class NpmPackageIndexBuilder {
"Derivation nvarchar NULL,\r\n"+
"PRIMARY KEY (FileName))\r\n");
psql = conn.prepareStatement("Insert into ResourceList (FileName, ResourceType, Id, Url, Version, Kind, Type, Supplements, Content, ValueSet, OIDs) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
psql = conn.prepareStatement("Insert into ResourceList (FileName, ResourceType, Id, Url, Version, Kind, Type, Supplements, Content, ValueSet) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
} catch (Exception e) {
if (conn != null) {
try {
@ -147,78 +137,6 @@ public class NpmPackageIndexBuilder {
return true;
}
public boolean seeOidsInFile(String name, byte[] content) {
if (name.endsWith(".json")) {
try {
JsonObject json = JsonParser.parseObject(content);
String rt = json.asString("resourceType");
if (rt != null) {
Set<String> oids = new HashSet<String>();
String url = null;
if ("NamingSystem".equals(rt)) {
for (JsonObject id : json.getJsonObjects("uniqueId")) {
String t = id.asString("type");
String v = id.asString("value");
if ("url".equals(t) && v != null) {
url = v;
} else if ("oid".equals(t) && v != null) {
oids.add(v);
}
}
if (url != null) {
JsonArray a = new JsonArray();
JsonObject cache = ("codesystem".equals(json.asString("kind")) ? oidIndexCS : oidIndexOther);
if (!cache.has(url)) {
json.add(url, a);
for (String s : oids) {
a.add(s);
}
}
}
} else {
if (json.hasPrimitive("url")) {
if (json.has("oid")) {
oids.add(json.asString("oid"));
}
if (json.has("url")) {
String v = json.asString("url");
if (v != null && v.startsWith("urn:oid:")) {
oids.add(v.substring(8));
}
}
for (JsonObject id : json.getJsonObjects("identifier")) {
String v = id.asString("value");
if (v != null && v.startsWith("urn:oid:")) {
oids.add(v.substring(8));
}
}
if (!oids.isEmpty()) {
JsonArray a = new JsonArray();
JsonObject cache = ("CodeSystem".equals(rt) ? oidIndexCS : oidIndexOther);
if (!cache.has(json.asString("url"))) {
cache.add(json.asString("url"), a);
for (String s : oids) {
a.add(s);
}
}
}
}
}
}
} catch (Exception e) {
System.out.println("Error parsing "+name+": "+e.getMessage());
e.printStackTrace();
if (name.contains("openapi")) {
return false;
}
}
}
return true;
}
public String build() {
try {
if (conn != null) {
@ -300,8 +218,5 @@ public class NpmPackageIndexBuilder {
return dbFilename;
}
public JsonObject getOidIndex() {
return oidIndex;
}
}

View File

@ -1066,3 +1066,4 @@ TERMINOLOGY_TX_UNKNOWN_OID = The OID ''{0}'' is not known
TERMINOLOGY_TX_SYSTEM_NO_CODE = A code with no system has no defined meaning, and it cannot be validated. A system should be provided
XSI_TYPE_WRONG = The xsi:type value ''{0}'' is wrong (should be ''{1}''). Note that xsi:type is unnecessary at this point
XSI_TYPE_UNNECESSARY = xsi:type is unnecessary at this point
TERMINOLOGY_TX_OID_MULTIPLE_MATCHES = The OID ''{0}'' matches multiple code systems ({1})

View File

@ -1651,13 +1651,17 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
String oid = element.getNamedChildValue("codeSystem");
if (oid != null) {
String url = context.urlForOid(true, oid);
if (url == null) {
Set<String> urls = context.urlsForOid(true, oid);
if (urls.size() != 1) {
c.setSystem("urn:oid:"+oid);
ok = false;
rule(errors, "2023-10-11", IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_UNKNOWN_OID, oid);
if (urls.size() == 0) {
rule(errors, "2023-10-11", IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_UNKNOWN_OID, oid);
} else {
rule(errors, "2023-10-11", IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_OID_MULTIPLE_MATCHES, oid, CommaSeparatedStringBuilder.join(",", urls));
}
} else {
c.setSystem(url);
c.setSystem(urls.iterator().next());
}
} else {
warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_SYSTEM_NO_CODE);
@ -6163,13 +6167,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
String code = element.getNamedChildValue(isPQ ? "unit" : "code");
String oid = element.getNamedChildValue("codeSystem");
if (oid != null) {
String url = context.urlForOid(true, oid);
if (url == null) {
Set<String> urls = context.urlsForOid(true, oid);
if (urls.size() != 1) {
system = "urn:oid:"+oid;
ok = false;
rule(errors, "2023-10-11", IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_UNKNOWN_OID, oid);
if (urls.size() == 0) {
rule(errors, "2023-10-11", IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_UNKNOWN_OID, oid);
} else {
rule(errors, "2023-10-11", IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_OID_MULTIPLE_MATCHES, oid, CommaSeparatedStringBuilder.join(",", urls));
}
} else {
system = url;
system = urls.iterator().next();
}
} else {
warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, code == null, I18nConstants.TERMINOLOGY_TX_SYSTEM_NO_CODE);