Merge pull request #1573 from hapifhir/2024-03-gg-valueset-validation
2024 03 gg valueset validation
This commit is contained in:
commit
6babf380c3
|
@ -0,0 +1,308 @@
|
|||
package org.hl7.fhir.convertors.misc;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
||||
import org.hl7.fhir.utilities.IniFile;
|
||||
import org.hl7.fhir.utilities.VersionUtilities;
|
||||
|
||||
public class OIDAssigner {
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
new OIDAssigner().execute(args[0], args[1], args[2]);
|
||||
}
|
||||
|
||||
private void execute(String oidSource, String folder, String version) {
|
||||
IniFile oids = new IniFile(oidSource);
|
||||
File f = new File(folder);
|
||||
process(oids, f, version);
|
||||
}
|
||||
|
||||
private void process(IniFile oids, File folder, String version) {
|
||||
for (File f : folder.listFiles()) {
|
||||
if (f.isDirectory()) {
|
||||
process(oids, f, version);
|
||||
} else if (f.getName().endsWith(".xml")) {
|
||||
processFile(oids, f, version, FhirFormat.XML);
|
||||
} else if (f.getName().endsWith(".json")) {
|
||||
processFile(oids, f, version, FhirFormat.JSON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processFile(IniFile oids, File f, String version, FhirFormat fmt) {
|
||||
switch (VersionUtilities.getMajMin(version)) {
|
||||
case "1.0" : processFileR2(oids, f, fmt);
|
||||
case "3.0" : processFileR3(oids, f, fmt);
|
||||
case "4.0" : processFileR4(oids, f, fmt);
|
||||
case "4.3" : processFileR4B(oids, f, fmt);
|
||||
case "5.0" : processFileR5(oids, f, fmt);
|
||||
}
|
||||
}
|
||||
|
||||
private void processFileR2(IniFile oids, File f, FhirFormat fmt) {
|
||||
org.hl7.fhir.dstu2.formats.IParser parser = fmt == FhirFormat.JSON ? new org.hl7.fhir.dstu2.formats.JsonParser() : new org.hl7.fhir.dstu2.formats.XmlParser();
|
||||
try {
|
||||
boolean save = false;
|
||||
org.hl7.fhir.dstu2.model.Resource r = parser.parse(new FileInputStream(f));
|
||||
if (r instanceof org.hl7.fhir.dstu2.model.ValueSet) {
|
||||
org.hl7.fhir.dstu2.model.ValueSet vs = (org.hl7.fhir.dstu2.model.ValueSet) r;
|
||||
boolean hasOid = isOid(vs.getIdentifier());
|
||||
if (!hasOid) {
|
||||
String oid = getOid(oids, "ValueSet", vs.getUrl());
|
||||
vs.setIdentifier(new org.hl7.fhir.dstu2.model.Identifier().setSystem("urn:ietf:rfc:3986").setValue("urn:oid:"+oid));
|
||||
save = true;
|
||||
}
|
||||
}
|
||||
if (r instanceof org.hl7.fhir.dstu2.model.ConceptMap) {
|
||||
org.hl7.fhir.dstu2.model.ConceptMap cm = (org.hl7.fhir.dstu2.model.ConceptMap) r;
|
||||
boolean hasOid = isOid(cm.getIdentifier());
|
||||
if (!hasOid) {
|
||||
String oid = getOid(oids, "ConceptMap", cm.getUrl());
|
||||
cm.setIdentifier(new org.hl7.fhir.dstu2.model.Identifier().setSystem("urn:ietf:rfc:3986").setValue("urn:oid:"+oid));
|
||||
save = true;
|
||||
}
|
||||
}
|
||||
if (r instanceof org.hl7.fhir.dstu2.model.StructureDefinition) {
|
||||
org.hl7.fhir.dstu2.model.StructureDefinition sd = (org.hl7.fhir.dstu2.model.StructureDefinition) r;
|
||||
boolean hasOid = false;
|
||||
for (org.hl7.fhir.dstu2.model.Identifier id : sd.getIdentifier()) {
|
||||
if (isOid(id)) {
|
||||
hasOid = true;
|
||||
}
|
||||
}
|
||||
if (!hasOid) {
|
||||
String oid = getOid(oids, "StructureDefinition", sd.getUrl());
|
||||
sd.getIdentifier().add(new org.hl7.fhir.dstu2.model.Identifier().setSystem("urn:ietf:rfc:3986").setValue("urn:oid:"+oid));
|
||||
save = true;
|
||||
}
|
||||
}
|
||||
if (save) {
|
||||
parser.setOutputStyle(org.hl7.fhir.dstu2.formats.IParser.OutputStyle.PRETTY).compose(new FileOutputStream(f), r);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Erro processing "+f.getAbsolutePath()+": "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void processFileR3(IniFile oids, File f, FhirFormat fmt) {
|
||||
org.hl7.fhir.dstu3.formats.IParser parser = fmt == FhirFormat.JSON ? new org.hl7.fhir.dstu3.formats.JsonParser() : new org.hl7.fhir.dstu3.formats.XmlParser();
|
||||
try {
|
||||
boolean save = false;
|
||||
org.hl7.fhir.dstu3.model.Resource r = parser.parse(new FileInputStream(f));
|
||||
if (r instanceof org.hl7.fhir.dstu3.model.CodeSystem) {
|
||||
org.hl7.fhir.dstu3.model.CodeSystem cs = (org.hl7.fhir.dstu3.model.CodeSystem) r;
|
||||
boolean hasOid = isOid(cs.getIdentifier());
|
||||
if (!hasOid) {
|
||||
String oid = getOid(oids, "CodeSystem", cs.getUrl());
|
||||
cs.setIdentifier(new org.hl7.fhir.dstu3.model.Identifier().setSystem("urn:ietf:rfc:3986").setValue("urn:oid:"+oid));
|
||||
save = true;
|
||||
}
|
||||
}
|
||||
if (r instanceof org.hl7.fhir.dstu3.model.ValueSet) {
|
||||
org.hl7.fhir.dstu3.model.ValueSet vs = (org.hl7.fhir.dstu3.model.ValueSet) r;
|
||||
boolean hasOid = false;
|
||||
for (org.hl7.fhir.dstu3.model.Identifier id : vs.getIdentifier()) {
|
||||
if (isOid(id)) {
|
||||
hasOid = true;
|
||||
}
|
||||
}
|
||||
if (!hasOid) {
|
||||
String oid = getOid(oids, "ValueSet", vs.getUrl());
|
||||
vs.getIdentifier().add(new org.hl7.fhir.dstu3.model.Identifier().setSystem("urn:ietf:rfc:3986").setValue("urn:oid:"+oid));
|
||||
save = true;
|
||||
}
|
||||
}
|
||||
if (r instanceof org.hl7.fhir.dstu3.model.ConceptMap) {
|
||||
org.hl7.fhir.dstu3.model.ConceptMap cm = (org.hl7.fhir.dstu3.model.ConceptMap) r;
|
||||
boolean hasOid = isOid(cm.getIdentifier());
|
||||
if (!hasOid) {
|
||||
String oid = getOid(oids, "ConceptMap", cm.getUrl());
|
||||
cm.setIdentifier(new org.hl7.fhir.dstu3.model.Identifier().setSystem("urn:ietf:rfc:3986").setValue("urn:oid:"+oid));
|
||||
save = true;
|
||||
}
|
||||
}
|
||||
if (r instanceof org.hl7.fhir.dstu3.model.StructureDefinition) {
|
||||
org.hl7.fhir.dstu3.model.StructureDefinition sd = (org.hl7.fhir.dstu3.model.StructureDefinition) r;
|
||||
boolean hasOid = false;
|
||||
for (org.hl7.fhir.dstu3.model.Identifier id : sd.getIdentifier()) {
|
||||
if (isOid(id)) {
|
||||
hasOid = true;
|
||||
}
|
||||
}
|
||||
if (!hasOid) {
|
||||
String oid = getOid(oids, "StructureDefinition", sd.getUrl());
|
||||
sd.getIdentifier().add(new org.hl7.fhir.dstu3.model.Identifier().setSystem("urn:ietf:rfc:3986").setValue("urn:oid:"+oid));
|
||||
save = true;
|
||||
}
|
||||
}
|
||||
if (save) {
|
||||
parser.setOutputStyle(org.hl7.fhir.dstu3.formats.IParser.OutputStyle.PRETTY).compose(new FileOutputStream(f), r);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Erro processing "+f.getAbsolutePath()+": "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void processFileR4(IniFile oids, File f, FhirFormat fmt) {
|
||||
org.hl7.fhir.r4.formats.IParser parser = fmt == FhirFormat.JSON ? new org.hl7.fhir.r4.formats.JsonParser() : new org.hl7.fhir.r4.formats.XmlParser();
|
||||
try {
|
||||
boolean save = false;
|
||||
org.hl7.fhir.r4.model.Resource r = parser.parse(new FileInputStream(f));
|
||||
if (r instanceof org.hl7.fhir.r4.model.CodeSystem) {
|
||||
org.hl7.fhir.r4.model.CodeSystem cs = (org.hl7.fhir.r4.model.CodeSystem) r;
|
||||
boolean hasOid = false;
|
||||
for (org.hl7.fhir.r4.model.Identifier id : cs.getIdentifier()) {
|
||||
if (isOid(id)) {
|
||||
hasOid = true;
|
||||
}
|
||||
}
|
||||
if (!hasOid) {
|
||||
String oid = getOid(oids, "CodeSystem", cs.getUrl());
|
||||
cs.getIdentifier().add(new org.hl7.fhir.r4.model.Identifier().setSystem("urn:ietf:rfc:3986").setValue("urn:oid:"+oid));
|
||||
save = true;
|
||||
}
|
||||
}
|
||||
if (r instanceof org.hl7.fhir.r4.model.ValueSet) {
|
||||
org.hl7.fhir.r4.model.ValueSet vs = (org.hl7.fhir.r4.model.ValueSet) r;
|
||||
boolean hasOid = false;
|
||||
for (org.hl7.fhir.r4.model.Identifier id : vs.getIdentifier()) {
|
||||
if (isOid(id)) {
|
||||
hasOid = true;
|
||||
}
|
||||
}
|
||||
if (!hasOid) {
|
||||
String oid = getOid(oids, "ValueSet", vs.getUrl());
|
||||
vs.getIdentifier().add(new org.hl7.fhir.r4.model.Identifier().setSystem("urn:ietf:rfc:3986").setValue("urn:oid:"+oid));
|
||||
save = true;
|
||||
}
|
||||
}
|
||||
if (r instanceof org.hl7.fhir.r4.model.ConceptMap) {
|
||||
org.hl7.fhir.r4.model.ConceptMap cm = (org.hl7.fhir.r4.model.ConceptMap) r;
|
||||
boolean hasOid = isOid(cm.getIdentifier());
|
||||
if (!hasOid) {
|
||||
String oid = getOid(oids, "ConceptMap", cm.getUrl());
|
||||
cm.setIdentifier(new org.hl7.fhir.r4.model.Identifier().setSystem("urn:ietf:rfc:3986").setValue("urn:oid:"+oid));
|
||||
save = true;
|
||||
}
|
||||
}
|
||||
if (r instanceof org.hl7.fhir.r4.model.StructureDefinition) {
|
||||
org.hl7.fhir.r4.model.StructureDefinition sd = (org.hl7.fhir.r4.model.StructureDefinition) r;
|
||||
boolean hasOid = false;
|
||||
for (org.hl7.fhir.r4.model.Identifier id : sd.getIdentifier()) {
|
||||
if (isOid(id)) {
|
||||
hasOid = true;
|
||||
}
|
||||
}
|
||||
if (!hasOid) {
|
||||
String oid = getOid(oids, "StructureDefinition", sd.getUrl());
|
||||
sd.getIdentifier().add(new org.hl7.fhir.r4.model.Identifier().setSystem("urn:ietf:rfc:3986").setValue("urn:oid:"+oid));
|
||||
save = true;
|
||||
}
|
||||
}
|
||||
if (save) {
|
||||
parser.setOutputStyle(org.hl7.fhir.r4.formats.IParser.OutputStyle.PRETTY).compose(new FileOutputStream(f), r);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Erro processing "+f.getAbsolutePath()+": "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void processFileR4B(IniFile oids, File f, FhirFormat fmt) {
|
||||
org.hl7.fhir.r4b.formats.IParser parser = fmt == FhirFormat.JSON ? new org.hl7.fhir.r4b.formats.JsonParser() : new org.hl7.fhir.r4b.formats.XmlParser();
|
||||
try {
|
||||
boolean save = false;
|
||||
org.hl7.fhir.r4b.model.Resource r = parser.parse(new FileInputStream(f));
|
||||
if (r instanceof org.hl7.fhir.r4b.model.CanonicalResource) {
|
||||
org.hl7.fhir.r4b.model.CanonicalResource cs = (org.hl7.fhir.r4b.model.CanonicalResource) r;
|
||||
boolean hasOid = false;
|
||||
for (org.hl7.fhir.r4b.model.Identifier id : cs.getIdentifier()) {
|
||||
if (isOid(id)) {
|
||||
hasOid = true;
|
||||
}
|
||||
}
|
||||
if (!hasOid) {
|
||||
String oid = getOid(oids, r.fhirType(), cs.getUrl());
|
||||
cs.getIdentifier().add(new org.hl7.fhir.r4b.model.Identifier().setSystem("urn:ietf:rfc:3986").setValue("urn:oid:"+oid));
|
||||
save = true;
|
||||
}
|
||||
}
|
||||
if (save) {
|
||||
parser.setOutputStyle(org.hl7.fhir.r4b.formats.IParser.OutputStyle.PRETTY).compose(new FileOutputStream(f), r);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Erro processing "+f.getAbsolutePath()+": "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void processFileR5(IniFile oids, File f, FhirFormat fmt) {
|
||||
org.hl7.fhir.r5.formats.IParser parser = fmt == FhirFormat.JSON ? new org.hl7.fhir.r5.formats.JsonParser() : new org.hl7.fhir.r5.formats.XmlParser();
|
||||
try {
|
||||
boolean save = false;
|
||||
org.hl7.fhir.r5.model.Resource r = parser.parse(new FileInputStream(f));
|
||||
if (r instanceof org.hl7.fhir.r5.model.CanonicalResource) {
|
||||
org.hl7.fhir.r5.model.CanonicalResource cs = (org.hl7.fhir.r5.model.CanonicalResource) r;
|
||||
boolean hasOid = false;
|
||||
for (org.hl7.fhir.r5.model.Identifier id : cs.getIdentifier()) {
|
||||
if (isOid(id)) {
|
||||
hasOid = true;
|
||||
}
|
||||
}
|
||||
if (!hasOid) {
|
||||
String oid = getOid(oids, r.fhirType(), cs.getUrl());
|
||||
cs.getIdentifier().add(new org.hl7.fhir.r5.model.Identifier().setSystem("urn:ietf:rfc:3986").setValue("urn:oid:"+oid));
|
||||
save = true;
|
||||
}
|
||||
}
|
||||
if (save) {
|
||||
parser.setOutputStyle(org.hl7.fhir.r5.formats.IParser.OutputStyle.PRETTY).compose(new FileOutputStream(f), r);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Erro processing "+f.getAbsolutePath()+": "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isOid(org.hl7.fhir.dstu2.model.Identifier id) {
|
||||
return "urn:ietf:rfc:3986".equals(id.getSystem()) && id.hasValue() && id.getValue().startsWith("urn:oid:");
|
||||
}
|
||||
|
||||
private boolean isOid(org.hl7.fhir.dstu3.model.Identifier id) {
|
||||
return "urn:ietf:rfc:3986".equals(id.getSystem()) && id.hasValue() && id.getValue().startsWith("urn:oid:");
|
||||
}
|
||||
|
||||
private boolean isOid(org.hl7.fhir.r4.model.Identifier id) {
|
||||
return "urn:ietf:rfc:3986".equals(id.getSystem()) && id.hasValue() && id.getValue().startsWith("urn:oid:");
|
||||
}
|
||||
|
||||
private boolean isOid(org.hl7.fhir.r4b.model.Identifier id) {
|
||||
return "urn:ietf:rfc:3986".equals(id.getSystem()) && id.hasValue() && id.getValue().startsWith("urn:oid:");
|
||||
}
|
||||
|
||||
private boolean isOid(org.hl7.fhir.r5.model.Identifier id) {
|
||||
return "urn:ietf:rfc:3986".equals(id.getSystem()) && id.hasValue() && id.getValue().startsWith("urn:oid:");
|
||||
}
|
||||
|
||||
private String getOid(IniFile oids, String rt, String url) {
|
||||
String root = oids.getStringProperty("Roots", rt);
|
||||
if (root == null) {
|
||||
throw new Error("no OID.ini entry for "+rt);
|
||||
}
|
||||
String oid = oids.getStringProperty(rt, url);
|
||||
if (oid != null) {
|
||||
return oid;
|
||||
}
|
||||
int key = oids.getIntegerProperty("Key", rt);
|
||||
key++;
|
||||
oid = root+"."+key;
|
||||
oids.setIntegerProperty("Key", rt, key, null);
|
||||
oids.setStringProperty(rt, url, oid, null);
|
||||
oids.save();
|
||||
return oid;
|
||||
}
|
||||
}
|
|
@ -388,7 +388,8 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
|
|||
|
||||
public List<String> getConcreteResources() {
|
||||
if (concreteResourceNames == null) {
|
||||
concreteResourceNames.addAll(Utilities.sorted(concreteResourceNameSet));
|
||||
concreteResourceNames = new ArrayList<>();
|
||||
concreteResourceNames.addAll(Utilities.sorted(getConcreteResourceSet()));
|
||||
}
|
||||
return concreteResourceNames;
|
||||
}
|
||||
|
|
|
@ -3253,7 +3253,7 @@ public class FHIRPathEngine {
|
|||
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
|
||||
}
|
||||
case SubsetOf : {
|
||||
checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, focus);
|
||||
checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, focus.toUnordered());
|
||||
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
|
||||
}
|
||||
case SupersetOf : {
|
||||
|
|
|
@ -405,6 +405,16 @@ public class TypeDetails {
|
|||
result.types.addAll(types);
|
||||
return result;
|
||||
}
|
||||
public TypeDetails toOrdered() {
|
||||
TypeDetails result = new TypeDetails(CollectionStatus.ORDERED);
|
||||
result.types.addAll(types);
|
||||
return result;
|
||||
}
|
||||
public TypeDetails toUnordered() {
|
||||
TypeDetails result = new TypeDetails(CollectionStatus.UNORDERED);
|
||||
result.types.addAll(types);
|
||||
return result;
|
||||
}
|
||||
public CollectionStatus getCollectionStatus() {
|
||||
return collectionStatus;
|
||||
}
|
||||
|
|
|
@ -8629,6 +8629,17 @@ public class ConceptMap extends MetadataResource {
|
|||
|
||||
}
|
||||
|
||||
public List<ConceptMapGroupComponent> getGroups(String su) {
|
||||
List<ConceptMapGroupComponent> res = new ArrayList<>();
|
||||
|
||||
for (ConceptMapGroupComponent g : getGroup()) {
|
||||
if (su.equals(g.getSource())) {
|
||||
res.add(g);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// end addition
|
||||
|
||||
|
||||
|
|
|
@ -159,10 +159,15 @@ public class DataRenderer extends Renderer implements CodeResolver {
|
|||
parts[0] = parts[0].substring(0, parts[0].indexOf("."));
|
||||
}
|
||||
StructureDefinition p = getContext().getWorker().fetchResource(StructureDefinition.class, parts[0]);
|
||||
if (p == null)
|
||||
if (p == null) {
|
||||
p = getContext().getWorker().fetchTypeDefinition(parts[0]);
|
||||
if (p == null)
|
||||
}
|
||||
if (context.getTypeMap().containsKey(parts[0])) {
|
||||
p = getContext().getWorker().fetchTypeDefinition(context.getTypeMap().get(parts[0]));
|
||||
}
|
||||
if (p == null) {
|
||||
p = getContext().getWorker().fetchResource(StructureDefinition.class, link);
|
||||
}
|
||||
if (p != null) {
|
||||
if ("Extension".equals(p.getType())) {
|
||||
path = null;
|
||||
|
@ -175,8 +180,9 @@ public class DataRenderer extends Renderer implements CodeResolver {
|
|||
if (url == null) {
|
||||
url = p.getUserString("filename");
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
throw new DefinitionException("Unable to resolve markdown link "+link);
|
||||
}
|
||||
|
||||
text = left+"["+link+"]("+url+(path == null ? "" : "#"+path)+")"+right;
|
||||
}
|
||||
|
@ -1201,6 +1207,11 @@ public class DataRenderer extends Renderer implements CodeResolver {
|
|||
|
||||
protected String displayIdentifier(Identifier ii) {
|
||||
String s = Utilities.noString(ii.getValue()) ? "?ngen-9?" : ii.getValue();
|
||||
if ("urn:ietf:rfc:3986".equals(ii.getSystem()) && s.startsWith("urn:oid:")) {
|
||||
s = "OID:"+s.substring(8);
|
||||
} else if ("urn:ietf:rfc:3986".equals(ii.getSystem()) && s.startsWith("urn:uuid:")) {
|
||||
s = "UUID:"+s.substring(9);
|
||||
} else {
|
||||
NamingSystem ns = context.getContext().getNSUrlMap().get(ii.getSystem());
|
||||
if (ns != null) {
|
||||
s = ns.present()+"#"+s;
|
||||
|
@ -1215,6 +1226,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
|
|||
} else if (ii.hasSystem()) {
|
||||
s = ii.getSystem()+"#"+s;
|
||||
}
|
||||
}
|
||||
|
||||
if (ii.hasUse() || ii.hasPeriod()) {
|
||||
s = s + "\u00A0(";
|
||||
|
|
|
@ -216,6 +216,7 @@ public class RenderingContext {
|
|||
private Map<KnownLinkType, String> links = new HashMap<>();
|
||||
private Map<String, String> namedLinks = new HashMap<>();
|
||||
private boolean addName = false;
|
||||
private Map<String, String> typeMap = new HashMap<>(); // type aliases that can be resolved in Markdown type links (mainly for cross-version usage)
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -281,6 +282,7 @@ public class RenderingContext {
|
|||
res.changeVersion = changeVersion;
|
||||
|
||||
res.terminologyServiceOptions = terminologyServiceOptions.copy();
|
||||
res.typeMap.putAll(typeMap);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -739,5 +741,9 @@ public class RenderingContext {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Map<String, String> getTypeMap() {
|
||||
return typeMap;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package org.hl7.fhir.r5.terminologies;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
|
@ -20,6 +21,7 @@ import org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent;
|
|||
import org.hl7.fhir.r5.model.Enumerations.ConceptMapRelationship;
|
||||
import org.hl7.fhir.r5.terminologies.ConceptMapUtilities.ConceptMapElementSorter;
|
||||
import org.hl7.fhir.r5.terminologies.ConceptMapUtilities.ElementMappingPair;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.r5.model.Identifier;
|
||||
import org.hl7.fhir.r5.model.Meta;
|
||||
import org.hl7.fhir.r5.model.UriType;
|
||||
|
@ -220,9 +222,9 @@ public class ConceptMapUtilities {
|
|||
res.setUrl(url);
|
||||
|
||||
for (ConceptMap cm : sequence) {
|
||||
if (res.hasTargetScope() && src.hasTargetScope()) {
|
||||
if (!cm.getSourceScope().equals(cm.getTargetScope())) {
|
||||
throw new Error("Mismatch between seqeuntial concept maps: ");
|
||||
if (res.hasTargetScope() && cm.hasTargetScope()) {
|
||||
if (!cm.getSourceScope().primitiveValue().equals(res.getTargetScope().primitiveValue())) {
|
||||
throw new Error("Mismatch between sequential concept maps: target was "+res.getTargetScope()+" and source is "+cm.getSourceScope());
|
||||
} else {
|
||||
res.setTargetScope(cm.getTargetScope());
|
||||
}
|
||||
|
@ -613,4 +615,71 @@ public class ConceptMapUtilities {
|
|||
return code.getSystem().equals(grp.getSource()) || (code.getSystem()+"|"+code.getVersion()).equals(grp.getSource());
|
||||
}
|
||||
|
||||
public static List<String> translateCode(String name, String defaultValue, ConceptMap... cmList) {
|
||||
List<String> res = translateCode(name, cmList);
|
||||
if (res.isEmpty()) {
|
||||
res.add(defaultValue);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
public static List<String> translateCode(String name, ConceptMap... cmList) {
|
||||
List<String> res = new ArrayList<>();
|
||||
res.add(name);
|
||||
for (ConceptMap cm : cmList) {
|
||||
res = translateCodes(res, cm);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private static List<String> translateCodes(List<String> codes, ConceptMap cm) {
|
||||
List<String> res = new ArrayList<>();
|
||||
for (ConceptMapGroupComponent g : cm.getGroup()) {
|
||||
for (SourceElementComponent e : g.getElement()) {
|
||||
if (Utilities.existsInList(e.getCode(), codes)) {
|
||||
for (TargetElementComponent t : e.getTarget()) {
|
||||
if (t.getRelationship() == ConceptMapRelationship.EQUIVALENT || t.getRelationship() == ConceptMapRelationship.RELATEDTO ||
|
||||
t.getRelationship() == ConceptMapRelationship.SOURCEISBROADERTHANTARGET ||t.getRelationship() == ConceptMapRelationship.SOURCEISNARROWERTHANTARGET) {
|
||||
res.add(t.getCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public static List<Coding> translateCoding(Coding code, ConceptMap... cmList) {
|
||||
List<Coding> res = new ArrayList<>();
|
||||
for (ConceptMap cm : cmList) {
|
||||
res = translateCodings(res, cm);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private static List<Coding> translateCodings(List<Coding> codes, ConceptMap cm) {
|
||||
List<Coding> res = new ArrayList<>();
|
||||
for (ConceptMapGroupComponent g : cm.getGroup()) {
|
||||
for (SourceElementComponent e : g.getElement()) {
|
||||
if (hasCode(g.getSource(), e.getCode(), codes)) {
|
||||
for (TargetElementComponent t : e.getTarget()) {
|
||||
if (t.getRelationship() == ConceptMapRelationship.EQUIVALENT || t.getRelationship() == ConceptMapRelationship.RELATEDTO ||
|
||||
t.getRelationship() == ConceptMapRelationship.SOURCEISBROADERTHANTARGET ||t.getRelationship() == ConceptMapRelationship.SOURCEISNARROWERTHANTARGET) {
|
||||
res.add(new Coding().setSystem(g.getTarget()).setCode((t.getCode())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private static boolean hasCode(String system, String code, List<Coding> codes) {
|
||||
for (Coding c : codes) {
|
||||
if (system.equals(c.getSystem()) && code.equals(c.getCode())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent;
|
|||
import org.hl7.fhir.r5.model.CodeSystem.PropertyType;
|
||||
import org.hl7.fhir.r5.model.Enumerations.FilterOperator;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
|
||||
public class KnownPropertyFilter extends ConceptFilter {
|
||||
|
||||
|
@ -31,10 +32,10 @@ public class KnownPropertyFilter extends ConceptFilter {
|
|||
case EQUAL: return filter.getValue().equals(v);
|
||||
case EXISTS: throw fail("not supported yet: "+filter.getOp().toCode());
|
||||
case GENERALIZES: throw fail("not supported yet: "+filter.getOp().toCode());
|
||||
case IN: throw fail("not supported yet: "+filter.getOp().toCode());
|
||||
case IN: return Utilities.existsInListTrimmed(v, filter.getValue().split("\\,"));
|
||||
case ISA: throw fail("not supported yet: "+filter.getOp().toCode());
|
||||
case ISNOTA: throw fail("not supported yet: "+filter.getOp().toCode());
|
||||
case NOTIN: throw fail("not supported yet: "+filter.getOp().toCode());
|
||||
case NOTIN: return Utilities.existsInListTrimmed(v, filter.getValue().split("\\,"));
|
||||
case NULL: throw fail("not supported yet: "+filter.getOp().toCode());
|
||||
case REGEX: throw fail("not supported yet: "+filter.getOp().toCode());
|
||||
default:
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent;
|
|||
import org.hl7.fhir.r5.model.CodeSystem.PropertyType;
|
||||
import org.hl7.fhir.r5.model.Enumerations.FilterOperator;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
|
||||
public class PropertyFilter extends ConceptFilter {
|
||||
|
||||
|
@ -31,17 +32,17 @@ public class PropertyFilter extends ConceptFilter {
|
|||
case EQUAL: return filter.getValue().equals(v);
|
||||
case EXISTS: throw fail("not supported yet: "+filter.getOp().toCode());
|
||||
case GENERALIZES: throw fail("not supported yet: "+filter.getOp().toCode());
|
||||
case IN: throw fail("not supported yet: "+filter.getOp().toCode());
|
||||
case IN: return Utilities.existsInListTrimmed(v, filter.getValue().split("\\,"));
|
||||
case ISA: throw fail("not supported yet: "+filter.getOp().toCode());
|
||||
case ISNOTA: throw fail("not supported yet: "+filter.getOp().toCode());
|
||||
case NOTIN: throw fail("not supported yet: "+filter.getOp().toCode());
|
||||
case NOTIN: return !Utilities.existsInListTrimmed(v, filter.getValue().split("\\,"));
|
||||
case NULL: throw fail("not supported yet: "+filter.getOp().toCode());
|
||||
case REGEX: throw fail("not supported yet: "+filter.getOp().toCode());
|
||||
default:
|
||||
throw fail("Shouldn't get here");
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
return filter.getOp() == FilterOperator.NOTIN;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -680,8 +680,6 @@ public class ValueSetExpander extends ValueSetProcessBase {
|
|||
if (debug) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set
|
||||
// that might fail too, but it might not, later.
|
||||
return new ValueSetExpansionOutcome(e.getMessage(), TerminologyServiceErrorClass.UNKNOWN, allErrors, e instanceof EFhirClientException || e instanceof TerminologyServiceException);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1395,6 +1395,36 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
}
|
||||
d = CodeSystemUtilities.getProperty(cs, code, f.getProperty());
|
||||
return d != null && d.primitiveValue() != null && d.primitiveValue().matches(f.getValue());
|
||||
case IN:
|
||||
if (f.getValue() == null) {
|
||||
return false;
|
||||
}
|
||||
String[] values = f.getValue().split("\\,");
|
||||
d = CodeSystemUtilities.getProperty(cs, code, f.getProperty());
|
||||
if (d != null) {
|
||||
String v = d.primitiveValue();
|
||||
for (String value : values) {
|
||||
if (v != null && v.equals(value.trim())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
case NOTIN:
|
||||
if (f.getValue() == null) {
|
||||
return true;
|
||||
}
|
||||
values = f.getValue().split("\\,");
|
||||
d = CodeSystemUtilities.getProperty(cs, code, f.getProperty());
|
||||
if (d != null) {
|
||||
String v = d.primitiveValue();
|
||||
for (String value : values) {
|
||||
if (v != null && v.equals(value.trim())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
System.out.println("todo: handle property filters with op = "+f.getOp());
|
||||
throw new FHIRException(context.formatMessage(I18nConstants.UNABLE_TO_HANDLE_SYSTEM__PROPERTY_FILTER_WITH_OP__, cs.getUrl(), f.getOp()));
|
||||
|
|
|
@ -52,6 +52,7 @@ import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
|||
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
||||
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.r5.model.ContactDetail;
|
||||
import org.hl7.fhir.r5.model.ContactPoint;
|
||||
import org.hl7.fhir.r5.model.ContactPoint.ContactPointSystem;
|
||||
|
@ -263,7 +264,10 @@ public class NPMPackageGenerator {
|
|||
packageManifest.add("fhirVersion", fv);
|
||||
packageManifest.add("date", dt);
|
||||
packageManifest.add("name", ig.getPackageId());
|
||||
|
||||
if (ig.hasJurisdiction() && ig.getJurisdiction().size() == 1 && ig.getJurisdictionFirstRep().getCoding().size() == 1) {
|
||||
Coding c = ig.getJurisdictionFirstRep().getCodingFirstRep();
|
||||
packageManifest.add("jurisdiction", c.getSystem()+"#"+c.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -933,6 +933,15 @@ public class Utilities {
|
|||
return false;
|
||||
}
|
||||
|
||||
public static boolean existsInListTrimmed(String value, String... array) {
|
||||
if (value == null)
|
||||
return false;
|
||||
for (String s : array)
|
||||
if (value.equals(s.trim()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean existsInList(int value, int... array) {
|
||||
for (int i : array)
|
||||
if (value == i)
|
||||
|
|
|
@ -300,8 +300,8 @@ public class VersionUtilities {
|
|||
} else if (Utilities.charCount(version, '.') == 2) {
|
||||
String[] p = version.split("\\.");
|
||||
return p[0]+"."+p[1];
|
||||
} else if (Utilities.existsInList(version, "R2", "R2B", "R3", "R4", "R4B", "R5", "R6")) {
|
||||
switch (version) {
|
||||
} else if (Utilities.existsInList(version.toUpperCase(), "R2", "R2B", "R3", "R4", "R4B", "R5", "R6")) {
|
||||
switch (version.toUpperCase()) {
|
||||
case "R2": return "1.0";
|
||||
case "R2B": return "1.4";
|
||||
case "R3": return "3.0";
|
||||
|
|
|
@ -1098,6 +1098,9 @@ public class I18nConstants {
|
|||
public static final String VALUESET_BAD_FILTER_VALUE_CODED = "VALUESET_BAD_FILTER_VALUE_CODED";
|
||||
public static final String VALUESET_BAD_FILTER_VALUE_CODED_INVALID = "VALUESET_BAD_FILTER_VALUE_CODED_INVALID";
|
||||
public static final String VALUESET_BAD_FILTER_OP = "VALUESET_BAD_FILTER_OP";
|
||||
public static final String VALUESET_BAD_FILTER_VALUE_HAS_COMMA = "VALUESET_BAD_FILTER_VALUE_HAS_COMMA";
|
||||
public static final String VALUESET_BAD_FILTER_VALUE_VALID_REGEX = "VALUESET_BAD_FILTER_VALUE_VALID_REGEX";
|
||||
public static final String VALUESET_BAD_PROPERTY_NO_REGEX = "VALUESET_BAD_PROPERTY_NO_REGEX";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1128,7 +1128,7 @@ VALUESET_INCLUDE_CS_SUPPLEMENT = The value set references CodeSystem ''{0}'' whi
|
|||
VALUESET_INCLUDE_CSVER_SUPPLEMENT = The value set references CodeSystem ''{0}'' version ''{2}'' which is a supplement. It must reference the underlying CodeSystem ''{1}'' and use the http://hl7.org/fhir/StructureDefinition/valueset-supplement extension for the supplement
|
||||
CODESYSTEM_SUPP_NO_DISPLAY = This display (''{0}'') differs from that defined by the base code system (''{1}''). Both displays claim to be 'the "primary designation" for the same language (''{2}''), and the correct interpretation of this is undefined
|
||||
CODESYSTEM_NOT_CONTAINED = CodeSystems are referred to directly from Coding.system, so it's generally best for them not to be contained resources
|
||||
CODESYSTEM_THO_CHECK = Most code systems defined in HL7 IGs will need to move to THO later during the process. Consider giving this code system a THO URL now (See https://confluence.hl7.org/display/TSMG/Terminology+Play+Book)
|
||||
CODESYSTEM_THO_CHECK = Most code systems defined in HL7 IGs will need to move to THO later during the process. Consider giving this code system a THO URL now (See https://confluence.hl7.org/display/TSMG/Terminology+Play+Book, and/or talk to TSMG)
|
||||
TYPE_SPECIFIC_CHECKS_DT_CANONICAL_MULTIPLE_POSSIBLE_VERSIONS = There are multiple different potential matches for the url ''{0}''. It might be a good idea to fix to the correct version to reduce the likelihood of a wrong version being selected by an implementation/implementer. Using version ''{1}'', found versions: {2}
|
||||
ABSTRACT_CODE_NOT_ALLOWED = Code ''{0}#{1}'' is abstract, and not allowed in this context
|
||||
CODESYSTEM_PROPERTY_DUPLICATE_URI = A property is already defined with the URI ''{0}''
|
||||
|
@ -1156,3 +1156,7 @@ VALUESET_BAD_FILTER_VALUE_VALID_CODE = The value for a filter based on property
|
|||
VALUESET_BAD_FILTER_VALUE_CODED = The value for a filter based on property ''{0}'' must be in the format system(|version)#code, not ''{1}''
|
||||
VALUESET_BAD_FILTER_VALUE_CODED_INVALID = The value for a filter based on property ''{0}'' is ''{1}'' which is not a valid code ({2})
|
||||
VALUESET_BAD_FILTER_OP = The operation ''{0}'' is not allowed for property ''{1}''. Allowed ops: {2}
|
||||
VALUESET_BAD_FILTER_VALUE_HAS_COMMA = The filter value has a comma, but the operation is different to 'in' and 'not-in', so the comma will be interpreted as part of the {0} value
|
||||
VALUESET_BAD_FILTER_VALUE_VALID_REGEX = The value for a filter based on property ''{0}'' should be a valid regex, not ''{1}'' (err = ''{2}'')
|
||||
VALUESET_BAD_PROPERTY_NO_REGEX = Cannot apply a regex filter to the property ''{0}'' (usually regex filters are applied to the codes, or a named property of the code system)
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@ import java.util.Collections;
|
|||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import org.hl7.fhir.r5.model.CodeSystem;
|
||||
import org.hl7.fhir.r5.model.CodeSystem.CodeSystemFilterComponent;
|
||||
|
@ -59,41 +61,46 @@ public class ValueSetValidator extends BaseValidator {
|
|||
|
||||
}
|
||||
|
||||
public enum CodeValidationRule {
|
||||
Warning, Error, None
|
||||
|
||||
}
|
||||
|
||||
public class PropertyValidationRules {
|
||||
boolean repeating;
|
||||
PropertyFilterType type;
|
||||
EnumSet<PropertyOperation> ops;
|
||||
protected PropertyValidationRules(boolean repeating, PropertyFilterType type, PropertyOperation... ops) {
|
||||
private PropertyFilterType type;
|
||||
private CodeValidationRule codeValidation;
|
||||
private EnumSet<PropertyOperation> ops;
|
||||
|
||||
protected PropertyValidationRules(PropertyFilterType type, CodeValidationRule codeValidation, PropertyOperation... ops) {
|
||||
super();
|
||||
this.repeating = repeating;
|
||||
this.type = type;
|
||||
this.codeValidation = codeValidation;
|
||||
this.ops = EnumSet.noneOf(PropertyOperation.class);
|
||||
for (PropertyOperation op : ops) {
|
||||
this.ops.add(op);
|
||||
}
|
||||
}
|
||||
public PropertyValidationRules(boolean repeating, PropertyFilterType type, EnumSet<PropertyOperation> ops) {
|
||||
public PropertyValidationRules(PropertyFilterType type, CodeValidationRule codeValidation, EnumSet<PropertyOperation> ops) {
|
||||
super();
|
||||
this.repeating = repeating;
|
||||
this.type = type;
|
||||
this.codeValidation = codeValidation;
|
||||
this.ops = ops;
|
||||
}
|
||||
|
||||
public boolean isRepeating() {
|
||||
return repeating;
|
||||
}
|
||||
public PropertyFilterType getType() {
|
||||
return type;
|
||||
}
|
||||
public EnumSet<PropertyOperation> getOps() {
|
||||
return ops;
|
||||
}
|
||||
|
||||
public CodeValidationRule getCodeValidation() {
|
||||
return codeValidation;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum PropertyFilterType {
|
||||
Boolean, Integer, Decimal, Code, DateTime, ValidCode, Coding
|
||||
Boolean, Integer, Decimal, Code, DateTime, Coding
|
||||
}
|
||||
|
||||
private static final int TOO_MANY_CODES_TO_VALIDATE = 1000;
|
||||
|
@ -422,13 +429,22 @@ public class ValueSetValidator extends BaseValidator {
|
|||
}
|
||||
|
||||
if ("exists".equals(op)) {
|
||||
ok = checkFilterValue(errors, stack, system, version, ok, property, value, PropertyFilterType.Boolean) && ok;
|
||||
} else if (rules.isRepeating()) {
|
||||
ok = checkFilterValue(errors, stack, system, version, ok, property, op, value, PropertyFilterType.Boolean, null) && ok;
|
||||
} else if ("regex".equals(op)) {
|
||||
String err = null;
|
||||
try {
|
||||
Pattern.compile(value);
|
||||
} catch (PatternSyntaxException e) {
|
||||
err = e.getMessage();
|
||||
}
|
||||
ok = rule(errors, "2024-03-09", IssueType.INVALID, stack, err == null, I18nConstants.VALUESET_BAD_FILTER_VALUE_VALID_REGEX, property, value, err) && ok;
|
||||
ok = rule(errors, "2024-03-09", IssueType.INVALID, stack, !"concept".equals(property), I18nConstants.VALUESET_BAD_PROPERTY_NO_REGEX, property) && ok;
|
||||
} else if (Utilities.existsInList(op, "in", "not-in")) {
|
||||
for (String v : value.split("\\,")) {
|
||||
ok = checkFilterValue(errors, stack, system, version, ok, property, v, rules.getType()) && ok;
|
||||
ok = checkFilterValue(errors, stack, system, version, ok, property, op, v, rules.getType(), rules.getCodeValidation()) && ok;
|
||||
}
|
||||
} else {
|
||||
ok = checkFilterValue(errors, stack, system, version, ok, property, value, rules.getType()) && ok;
|
||||
ok = checkFilterValue(errors, stack, system, version, ok, property, op, value, rules.getType(), rules.getCodeValidation()) && ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -454,8 +470,11 @@ public class ValueSetValidator extends BaseValidator {
|
|||
return false;
|
||||
}
|
||||
|
||||
private boolean checkFilterValue(List<ValidationMessage> errors, NodeStack stack, String system, String version,boolean ok, String property, String value, PropertyFilterType type) {
|
||||
private boolean checkFilterValue(List<ValidationMessage> errors, NodeStack stack, String system, String version,boolean ok, String property, String op, String value, PropertyFilterType type, CodeValidationRule cr) {
|
||||
if (type != null) {
|
||||
if (!Utilities.existsInList(op, "in", "not-in")) {
|
||||
hint(errors, "2024-03-09", IssueType.INVALID, stack.getLiteralPath(), !value.contains(","), I18nConstants.VALUESET_BAD_FILTER_VALUE_HAS_COMMA, type.toString());
|
||||
}
|
||||
switch (type) {
|
||||
case Boolean:
|
||||
ok = rule(errors, "2024-03-09", IssueType.INVALID, stack,
|
||||
|
@ -466,6 +485,14 @@ public class ValueSetValidator extends BaseValidator {
|
|||
ok = rule(errors, "2024-03-09", IssueType.INVALID, stack,
|
||||
value.trim().equals(value),
|
||||
I18nConstants.VALUESET_BAD_FILTER_VALUE_CODE, property, value) && ok;
|
||||
if (cr == CodeValidationRule.Error || cr == CodeValidationRule.Warning) {
|
||||
ValidationResult vr = context.validateCode(baseOptions, system, version, value, null);
|
||||
if (cr == CodeValidationRule.Error) {
|
||||
ok = rule(errors, "2024-03-09", IssueType.INVALID, stack.getLiteralPath(), vr.isOk(), I18nConstants.VALUESET_BAD_FILTER_VALUE_VALID_CODE, property, value, system, vr.getMessage()) && ok;
|
||||
} else {
|
||||
warning(errors, "2024-03-09", IssueType.INVALID, stack.getLiteralPath(), vr.isOk(), I18nConstants.VALUESET_BAD_FILTER_VALUE_VALID_CODE, property, value, system, vr.getMessage());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DateTime:
|
||||
ok = rule(errors, "2024-03-09", IssueType.INVALID, stack.getLiteralPath(),
|
||||
|
@ -482,18 +509,12 @@ public class ValueSetValidator extends BaseValidator {
|
|||
Utilities.isInteger(value),
|
||||
I18nConstants.VALUESET_BAD_FILTER_VALUE_INTEGER, property, value) && ok;
|
||||
break;
|
||||
case ValidCode :
|
||||
ValidationResult vr = context.validateCode(baseOptions, system, version, value, null);
|
||||
ok = rule(errors, "2024-03-09", IssueType.INVALID, stack.getLiteralPath(),
|
||||
vr.isOk(),
|
||||
I18nConstants.VALUESET_BAD_FILTER_VALUE_VALID_CODE, property, value, system, vr.getMessage()) && ok;
|
||||
break;
|
||||
case Coding :
|
||||
Coding code = Coding.fromLiteral(value);
|
||||
if (code == null) {
|
||||
ok = rule(errors, "2024-03-09", IssueType.INVALID, stack, false, I18nConstants.VALUESET_BAD_FILTER_VALUE_CODED, property, value) && ok;
|
||||
} else {
|
||||
vr = context.validateCode(baseOptions, code, null);
|
||||
ValidationResult vr = context.validateCode(baseOptions, code, null);
|
||||
ok = rule(errors, "2024-03-09", IssueType.INVALID, stack, vr.isOk(), I18nConstants.VALUESET_BAD_FILTER_VALUE_CODED_INVALID, property, value, vr.getMessage()) && ok;
|
||||
}
|
||||
break;
|
||||
|
@ -521,12 +542,16 @@ public class ValueSetValidator extends BaseValidator {
|
|||
if (property.equals(p.getCode())) {
|
||||
if (p.getType() != null) {
|
||||
switch (p.getType()) {
|
||||
case BOOLEAN: return new PropertyValidationRules(false, PropertyFilterType.Boolean, ops);
|
||||
case CODE: return new PropertyValidationRules(false, PropertyFilterType.ValidCode, ops);
|
||||
case CODING: return new PropertyValidationRules(false, PropertyFilterType.Coding, ops);
|
||||
case DATETIME: return new PropertyValidationRules(false, PropertyFilterType.DateTime, ops);
|
||||
case DECIMAL: return new PropertyValidationRules(false, PropertyFilterType.Decimal, ops);
|
||||
case INTEGER: return new PropertyValidationRules(false, PropertyFilterType.Integer, ops);
|
||||
case BOOLEAN: return new PropertyValidationRules(PropertyFilterType.Boolean, null, ops);
|
||||
case CODE:
|
||||
// the definitions say " a code that identifies a concept defined in the code system" -> ValidCode.
|
||||
// but many people have ignored that and defined a property as 'code' because it's from a list of values that are otherwise undefined
|
||||
boolean external = !forPublication || cs.getWebPath() == null || Utilities.isAbsoluteUrl(cs.getWebPath());
|
||||
return new PropertyValidationRules(PropertyFilterType.Code, external ? CodeValidationRule.Warning : CodeValidationRule.Error, ops); // valid code... the definitions say that, but people were missing that in the pastm
|
||||
case CODING: return new PropertyValidationRules(PropertyFilterType.Coding, null, ops);
|
||||
case DATETIME: return new PropertyValidationRules(PropertyFilterType.DateTime, null, ops);
|
||||
case DECIMAL: return new PropertyValidationRules(PropertyFilterType.Decimal, null, ops);
|
||||
case INTEGER: return new PropertyValidationRules(PropertyFilterType.Integer, null, ops);
|
||||
case STRING: return null;
|
||||
}
|
||||
}
|
||||
|
@ -535,51 +560,51 @@ public class ValueSetValidator extends BaseValidator {
|
|||
}
|
||||
|
||||
switch (property) {
|
||||
case "concept" : return new PropertyValidationRules(false, PropertyFilterType.ValidCode, addToOps(ops, PropertyOperation.Equals, PropertyOperation.In, PropertyOperation.IsA, PropertyOperation.DescendentOf, PropertyOperation.DescendentLeaf, PropertyOperation.IsNotA, PropertyOperation.NotIn));
|
||||
case "code" : return new PropertyValidationRules(false, PropertyFilterType.ValidCode, addToOps(ops, PropertyOperation.Equals, PropertyOperation.RegEx));
|
||||
case "status" : return new PropertyValidationRules(false, PropertyFilterType.Code, ops);
|
||||
case "inactive" : return new PropertyValidationRules(false, PropertyFilterType.Boolean, ops);
|
||||
case "effectiveDate" : return new PropertyValidationRules(false, PropertyFilterType.DateTime, ops);
|
||||
case "deprecationDate" : return new PropertyValidationRules(false, PropertyFilterType.DateTime, ops);
|
||||
case "retirementDate" : return new PropertyValidationRules(false, PropertyFilterType.DateTime, ops);
|
||||
case "notSelectable" : return new PropertyValidationRules(false, PropertyFilterType.Boolean, ops);
|
||||
case "parent" : return new PropertyValidationRules(false, PropertyFilterType.ValidCode, ops);
|
||||
case "child" : return new PropertyValidationRules(false, PropertyFilterType.ValidCode, ops);
|
||||
case "partOf" : return new PropertyValidationRules(false, PropertyFilterType.ValidCode, ops);
|
||||
case "synonym" : return new PropertyValidationRules(false, PropertyFilterType.Code, ops);
|
||||
case "concept" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, addToOps(ops, PropertyOperation.Equals, PropertyOperation.In, PropertyOperation.IsA, PropertyOperation.DescendentOf, PropertyOperation.DescendentLeaf, PropertyOperation.IsNotA, PropertyOperation.NotIn));
|
||||
case "code" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, addToOps(ops, PropertyOperation.Equals, PropertyOperation.RegEx));
|
||||
case "status" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, ops);
|
||||
case "inactive" : return new PropertyValidationRules(PropertyFilterType.Boolean,null, ops);
|
||||
case "effectiveDate" : return new PropertyValidationRules(PropertyFilterType.DateTime, null, ops);
|
||||
case "deprecationDate" : return new PropertyValidationRules(PropertyFilterType.DateTime, null, ops);
|
||||
case "retirementDate" : return new PropertyValidationRules(PropertyFilterType.DateTime, null, ops);
|
||||
case "notSelectable" : return new PropertyValidationRules(PropertyFilterType.Boolean, null, ops);
|
||||
case "parent" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, ops);
|
||||
case "child" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, ops);
|
||||
case "partOf" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, ops);
|
||||
case "synonym" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, ops); // ? none?
|
||||
case "comment" : return null;
|
||||
case "itemWeight" : return new PropertyValidationRules(false, PropertyFilterType.Decimal, ops);
|
||||
case "itemWeight" : return new PropertyValidationRules(PropertyFilterType.Decimal, null, ops);
|
||||
}
|
||||
switch (system) {
|
||||
case "http://loinc.org" :
|
||||
if (Utilities.existsInList(property, "copyright", "STATUS", "CLASS", "CONSUMER_NAME", "ORDER_OBS", "DOCUMENT_SECTION")) {
|
||||
return new PropertyValidationRules(false, PropertyFilterType.Code);
|
||||
if (Utilities.existsInList(property, "copyright", "STATUS", "CLASS", "CONSUMER_NAME", "ORDER_OBS", "DOCUMENT_SECTION", "SCALE_TYP")) {
|
||||
return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None);
|
||||
} else if ("CLASSTYPE".equals(property)) {
|
||||
return new PropertyValidationRules(false, PropertyFilterType.Integer, addToOps(ops, PropertyOperation.Equals, PropertyOperation.In));
|
||||
return new PropertyValidationRules(PropertyFilterType.Integer, null, addToOps(ops, PropertyOperation.Equals, PropertyOperation.In));
|
||||
} else {
|
||||
return new PropertyValidationRules(false, PropertyFilterType.ValidCode, addToOps(ops, PropertyOperation.Equals, PropertyOperation.In));
|
||||
return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, addToOps(ops, PropertyOperation.Equals, PropertyOperation.In));
|
||||
}
|
||||
case "http://snomed.info/sct":
|
||||
switch (property) {
|
||||
case "constraint": return null; // for now
|
||||
case "expressions": return new PropertyValidationRules(false, PropertyFilterType.Boolean, addToOps(ops, PropertyOperation.Equals, PropertyOperation.In));
|
||||
case "expressions": return new PropertyValidationRules(PropertyFilterType.Boolean, null, addToOps(ops, PropertyOperation.Equals, PropertyOperation.In));
|
||||
default:
|
||||
return new PropertyValidationRules(false, PropertyFilterType.ValidCode, addToOps(ops, PropertyOperation.Equals, PropertyOperation.In));
|
||||
return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, addToOps(ops, PropertyOperation.Equals, PropertyOperation.In));
|
||||
}
|
||||
case "http://www.nlm.nih.gov/research/umls/rxnorm" : return new PropertyValidationRules(false, PropertyFilterType.Code, ops);
|
||||
case "http://unitsofmeasure.org" : return new PropertyValidationRules(false, PropertyFilterType.Code, ops);
|
||||
case "http://www.nlm.nih.gov/research/umls/rxnorm" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, ops);
|
||||
case "http://unitsofmeasure.org" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, ops);
|
||||
case "http://www.ama-assn.org/go/cpt" :
|
||||
switch (property) {
|
||||
case "modifier": return new PropertyValidationRules(false, PropertyFilterType.Boolean, ops);
|
||||
case "kind" : return new PropertyValidationRules(true, PropertyFilterType.Code, ops); // for now
|
||||
case "modified": return new PropertyValidationRules(false, PropertyFilterType.Boolean, ops);
|
||||
case "modifier": return new PropertyValidationRules(PropertyFilterType.Boolean, null, ops);
|
||||
case "kind" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, ops); // for now
|
||||
case "modified": return new PropertyValidationRules(PropertyFilterType.Boolean, null, ops);
|
||||
case "code" : return null;
|
||||
case "telemedicine": return new PropertyValidationRules(false, PropertyFilterType.Boolean, ops);
|
||||
case "orthopox" : return new PropertyValidationRules(false, PropertyFilterType.Boolean, ops);
|
||||
case "telemedicine": return new PropertyValidationRules(PropertyFilterType.Boolean, null, ops);
|
||||
case "orthopox" : return new PropertyValidationRules(PropertyFilterType.Boolean,null, ops);
|
||||
}
|
||||
}
|
||||
if (ops != null) {
|
||||
return new PropertyValidationRules(false, null, ops);
|
||||
return new PropertyValidationRules(null, null, ops);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -20,7 +20,7 @@
|
|||
<properties>
|
||||
<guava_version>32.0.1-jre</guava_version>
|
||||
<hapi_fhir_version>6.4.1</hapi_fhir_version>
|
||||
<validator_test_case_version>1.5.1</validator_test_case_version>
|
||||
<validator_test_case_version>1.5.2-SNAPSHOT</validator_test_case_version>
|
||||
<jackson_version>2.16.0</jackson_version>
|
||||
<junit_jupiter_version>5.9.2</junit_jupiter_version>
|
||||
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>
|
||||
|
|
Loading…
Reference in New Issue