Merge pull request #1149 from hapifhir/gg-202303-cm-sm-validation
Gg 202303 cm sm validation
This commit is contained in:
commit
58ad99cfd3
|
@ -1,7 +1,11 @@
|
|||
## Validator Changes
|
||||
|
||||
* no changes
|
||||
* Add ConceptMap validation
|
||||
* Add StructureMap validation
|
||||
* Validate using type regex (had been omitted to now, mostly affects decimal validation)
|
||||
|
||||
## Other code changes
|
||||
|
||||
* no changes
|
||||
* Various fixes and utilities to support StructureMap & ConceptMap validation
|
||||
* Fix Observation.value conversion between R5 and other versions
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.hl7.fhir.convertors.conv10_50.resources10_50;
|
||||
|
||||
import org.hl7.fhir.convertors.context.ConversionContext10_50;
|
||||
import org.hl7.fhir.convertors.context.ConversionContext43_50;
|
||||
import org.hl7.fhir.convertors.conv10_50.datatypes10_50.Reference10_50;
|
||||
import org.hl7.fhir.convertors.conv10_50.datatypes10_50.complextypes10_50.CodeableConcept10_50;
|
||||
import org.hl7.fhir.convertors.conv10_50.datatypes10_50.complextypes10_50.Identifier10_50;
|
||||
|
@ -8,6 +9,7 @@ import org.hl7.fhir.convertors.conv10_50.datatypes10_50.complextypes10_50.Range1
|
|||
import org.hl7.fhir.convertors.conv10_50.datatypes10_50.complextypes10_50.SimpleQuantity10_50;
|
||||
import org.hl7.fhir.convertors.conv10_50.datatypes10_50.primitivetypes10_50.Instant10_50;
|
||||
import org.hl7.fhir.convertors.conv10_50.datatypes10_50.primitivetypes10_50.String10_50;
|
||||
import org.hl7.fhir.convertors.conv43_50.datatypes43_50.primitive43_50.String43_50;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
|
||||
public class Observation10_50 {
|
||||
|
@ -34,8 +36,15 @@ public class Observation10_50 {
|
|||
if (src.hasIssuedElement())
|
||||
tgt.setIssuedElement(Instant10_50.convertInstant(src.getIssuedElement()));
|
||||
for (org.hl7.fhir.r5.model.Reference t : src.getPerformer()) tgt.addPerformer(Reference10_50.convertReference(t));
|
||||
if (src.hasValue())
|
||||
tgt.setValue(ConversionContext10_50.INSTANCE.getVersionConvertor_10_50().convertType(src.getValue()));
|
||||
|
||||
if (src.hasValue()) {
|
||||
if (src.hasValueMarkdownType()) {
|
||||
tgt.setValue(String10_50.convertMarkdownToString(src.getValueMarkdownType()));
|
||||
} else {
|
||||
tgt.setValue(ConversionContext10_50.INSTANCE.getVersionConvertor_10_50().convertType(src.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
if (src.hasDataAbsentReason())
|
||||
tgt.setDataAbsentReason(CodeableConcept10_50.convertCodeableConcept(src.getDataAbsentReason()));
|
||||
if (src.hasInterpretation())
|
||||
|
@ -84,8 +93,15 @@ public class Observation10_50 {
|
|||
tgt.setIssuedElement(Instant10_50.convertInstant(src.getIssuedElement()));
|
||||
for (org.hl7.fhir.dstu2.model.Reference t : src.getPerformer())
|
||||
tgt.addPerformer(Reference10_50.convertReference(t));
|
||||
if (src.hasValue())
|
||||
tgt.setValue(ConversionContext10_50.INSTANCE.getVersionConvertor_10_50().convertType(src.getValue()));
|
||||
|
||||
if (src.hasValue()) {
|
||||
if (src.hasValueStringType()) {
|
||||
tgt.setValue(String10_50.convertStringToMarkdown(src.getValueStringType()));
|
||||
} else {
|
||||
tgt.setValue(ConversionContext10_50.INSTANCE.getVersionConvertor_10_50().convertType(src.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
if (src.hasDataAbsentReason())
|
||||
tgt.setDataAbsentReason(CodeableConcept10_50.convertCodeableConcept(src.getDataAbsentReason()));
|
||||
if (src.hasInterpretation())
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.hl7.fhir.convertors.conv30_50.resources30_50;
|
||||
|
||||
import org.hl7.fhir.convertors.context.ConversionContext30_50;
|
||||
import org.hl7.fhir.convertors.context.ConversionContext43_50;
|
||||
import org.hl7.fhir.convertors.conv30_50.datatypes30_50.Reference30_50;
|
||||
import org.hl7.fhir.convertors.conv30_50.datatypes30_50.complextypes30_50.CodeableConcept30_50;
|
||||
import org.hl7.fhir.convertors.conv30_50.datatypes30_50.complextypes30_50.Identifier30_50;
|
||||
|
@ -8,6 +9,7 @@ import org.hl7.fhir.convertors.conv30_50.datatypes30_50.complextypes30_50.Range3
|
|||
import org.hl7.fhir.convertors.conv30_50.datatypes30_50.complextypes30_50.SimpleQuantity30_50;
|
||||
import org.hl7.fhir.convertors.conv30_50.datatypes30_50.primitivetypes30_50.Instant30_50;
|
||||
import org.hl7.fhir.convertors.conv30_50.datatypes30_50.primitivetypes30_50.String30_50;
|
||||
import org.hl7.fhir.convertors.conv43_50.datatypes43_50.primitive43_50.String43_50;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
|
||||
public class Observation30_50 {
|
||||
|
@ -35,8 +37,15 @@ public class Observation30_50 {
|
|||
if (src.hasIssued())
|
||||
tgt.setIssuedElement(Instant30_50.convertInstant(src.getIssuedElement()));
|
||||
for (org.hl7.fhir.r5.model.Reference t : src.getPerformer()) tgt.addPerformer(Reference30_50.convertReference(t));
|
||||
if (src.hasValue())
|
||||
tgt.setValue(ConversionContext30_50.INSTANCE.getVersionConvertor_30_50().convertType(src.getValue()));
|
||||
|
||||
if (src.hasValue()) {
|
||||
if (src.hasValueMarkdownType()) {
|
||||
tgt.setValue(String30_50.convertMarkdownToString(src.getValueMarkdownType()));
|
||||
} else {
|
||||
tgt.setValue(ConversionContext30_50.INSTANCE.getVersionConvertor_30_50().convertType(src.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
if (src.hasDataAbsentReason())
|
||||
tgt.setDataAbsentReason(CodeableConcept30_50.convertCodeableConcept(src.getDataAbsentReason()));
|
||||
if (src.hasInterpretation())
|
||||
|
@ -86,8 +95,15 @@ public class Observation30_50 {
|
|||
tgt.setIssuedElement(Instant30_50.convertInstant(src.getIssuedElement()));
|
||||
for (org.hl7.fhir.dstu3.model.Reference t : src.getPerformer())
|
||||
tgt.addPerformer(Reference30_50.convertReference(t));
|
||||
if (src.hasValue())
|
||||
tgt.setValue(ConversionContext30_50.INSTANCE.getVersionConvertor_30_50().convertType(src.getValue()));
|
||||
|
||||
if (src.hasValue()) {
|
||||
if (src.hasValueStringType()) {
|
||||
tgt.setValue(String30_50.convertStringToMarkdown(src.getValueStringType()));
|
||||
} else {
|
||||
tgt.setValue(ConversionContext30_50.INSTANCE.getVersionConvertor_30_50().convertType(src.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
if (src.hasDataAbsentReason())
|
||||
tgt.setDataAbsentReason(CodeableConcept30_50.convertCodeableConcept(src.getDataAbsentReason()));
|
||||
if (src.hasInterpretation())
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package org.hl7.fhir.convertors.conv43_50.resources43_50;
|
||||
|
||||
import org.hl7.fhir.convertors.context.ConversionContext40_50;
|
||||
import org.hl7.fhir.convertors.context.ConversionContext43_50;
|
||||
import org.hl7.fhir.convertors.conv40_50.datatypes40_50.primitive40_50.String40_50;
|
||||
import org.hl7.fhir.convertors.conv43_50.datatypes43_50.general43_50.Annotation43_50;
|
||||
import org.hl7.fhir.convertors.conv43_50.datatypes43_50.general43_50.CodeableConcept43_50;
|
||||
import org.hl7.fhir.convertors.conv43_50.datatypes43_50.general43_50.Identifier43_50;
|
||||
|
@ -67,8 +69,13 @@ public class Observation43_50 {
|
|||
if (src.hasIssued())
|
||||
tgt.setIssuedElement(Instant43_50.convertInstant(src.getIssuedElement()));
|
||||
for (org.hl7.fhir.r4b.model.Reference t : src.getPerformer()) tgt.addPerformer(Reference43_50.convertReference(t));
|
||||
if (src.hasValue())
|
||||
tgt.setValue(ConversionContext43_50.INSTANCE.getVersionConvertor_43_50().convertType(src.getValue()));
|
||||
if (src.hasValue()) {
|
||||
if (src.hasValueStringType()) {
|
||||
tgt.setValue(String43_50.convertStringToMarkdown(src.getValueStringType()));
|
||||
} else {
|
||||
tgt.setValue(ConversionContext43_50.INSTANCE.getVersionConvertor_43_50().convertType(src.getValue()));
|
||||
}
|
||||
}
|
||||
if (src.hasDataAbsentReason())
|
||||
tgt.setDataAbsentReason(CodeableConcept43_50.convertCodeableConcept(src.getDataAbsentReason()));
|
||||
for (org.hl7.fhir.r4b.model.CodeableConcept t : src.getInterpretation())
|
||||
|
@ -117,8 +124,13 @@ public class Observation43_50 {
|
|||
if (src.hasIssued())
|
||||
tgt.setIssuedElement(Instant43_50.convertInstant(src.getIssuedElement()));
|
||||
for (org.hl7.fhir.r5.model.Reference t : src.getPerformer()) tgt.addPerformer(Reference43_50.convertReference(t));
|
||||
if (src.hasValue())
|
||||
tgt.setValue(ConversionContext43_50.INSTANCE.getVersionConvertor_43_50().convertType(src.getValue()));
|
||||
if (src.hasValue()) {
|
||||
if (src.hasValueMarkdownType()) {
|
||||
tgt.setValue(String43_50.convertMarkdownToString(src.getValueMarkdownType()));
|
||||
} else {
|
||||
tgt.setValue(ConversionContext43_50.INSTANCE.getVersionConvertor_43_50().convertType(src.getValue()));
|
||||
}
|
||||
}
|
||||
if (src.hasDataAbsentReason())
|
||||
tgt.setDataAbsentReason(CodeableConcept43_50.convertCodeableConcept(src.getDataAbsentReason()));
|
||||
for (org.hl7.fhir.r5.model.CodeableConcept t : src.getInterpretation())
|
||||
|
|
|
@ -12,8 +12,10 @@ import org.hl7.fhir.exceptions.DefinitionException;
|
|||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.elementmodel.Element.SpecialElement;
|
||||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||
import org.hl7.fhir.r5.model.ExpressionNode;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||
import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupUnmappedMode;
|
||||
import org.hl7.fhir.r5.model.Enumerations.ConceptMapRelationship;
|
||||
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
|
||||
|
@ -78,6 +80,10 @@ public class FmlParser extends ParserBase {
|
|||
}
|
||||
}
|
||||
lexer.setMetadataFormat(false);
|
||||
if (!result.hasChild("status")) {
|
||||
result.makeElement("status").setValue("draft");
|
||||
}
|
||||
|
||||
while (lexer.hasToken("conceptmap"))
|
||||
parseConceptMap(result, lexer);
|
||||
|
||||
|
@ -86,6 +92,9 @@ public class FmlParser extends ParserBase {
|
|||
while (lexer.hasToken("imports"))
|
||||
parseImports(result, lexer);
|
||||
|
||||
while (lexer.hasToken("conceptmap"))
|
||||
parseConceptMap(result, lexer);
|
||||
|
||||
while (!lexer.done()) {
|
||||
parseGroup(result, lexer);
|
||||
}
|
||||
|
@ -94,24 +103,22 @@ public class FmlParser extends ParserBase {
|
|||
} catch (Exception e) {
|
||||
logError("2023-02-24", -1, -1, "?", IssueType.INVALID, e.getMessage(), IssueSeverity.FATAL);
|
||||
}
|
||||
|
||||
if (!result.hasChild("status")) {
|
||||
result.makeElement("status").setValue("draft");
|
||||
}
|
||||
result.setIgnorePropertyOrder(true);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void parseConceptMap(Element result, FHIRLexer lexer) throws FHIRLexerException {
|
||||
private void parseConceptMap(Element structureMap, FHIRLexer lexer) throws FHIRLexerException {
|
||||
lexer.token("conceptmap");
|
||||
Element map = Manager.build(context, context.fetchTypeDefinition("ConceptMap"));
|
||||
Element map = structureMap.makeElement("contained");
|
||||
StructureDefinition sd = context.fetchTypeDefinition("ConceptMap");
|
||||
map.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd), SpecialElement.fromProperty(map.getProperty()), map.getProperty());
|
||||
map.setType("ConceptMap");
|
||||
Element eid = map.makeElement("id").markLocation(lexer.getCurrentLocation());
|
||||
String id = lexer.readConstant("map id");
|
||||
if (id.startsWith("#"))
|
||||
throw lexer.error("Concept Map identifier must start with #");
|
||||
eid.setValue(id);
|
||||
map.makeElement("status").setValue(PublicationStatus.DRAFT.toCode()); // todo: how to add this to the text format
|
||||
result.makeElement("contained").setElement("resource", map);
|
||||
map.makeElement("status").setValue(structureMap.getChildValue("status"));
|
||||
lexer.token("{");
|
||||
// lexer.token("source");
|
||||
// map.setSource(new UriType(lexer.readConstant("source")));
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.hl7.fhir.r5.model.CanonicalResource;
|
|||
import org.hl7.fhir.r5.model.CanonicalType;
|
||||
import org.hl7.fhir.r5.model.CodeSystem;
|
||||
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
|
||||
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionDesignationComponent;
|
||||
import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent;
|
||||
import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent;
|
||||
import org.hl7.fhir.r5.model.CodeSystem.PropertyType;
|
||||
|
@ -414,16 +415,6 @@ public class CodeSystemUtilities {
|
|||
return cs;
|
||||
}
|
||||
|
||||
public static boolean makeCSShareable(CodeSystem cs) {
|
||||
if (!cs.hasMeta())
|
||||
cs.setMeta(new Meta());
|
||||
for (UriType t : cs.getMeta().getProfile())
|
||||
if ("http://hl7.org/fhir/StructureDefinition/shareablecodesystem".equals(t.getValue()))
|
||||
return false;
|
||||
cs.getMeta().getProfile().add(new CanonicalType("http://hl7.org/fhir/StructureDefinition/shareablecodesystem"));
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void setOID(CodeSystem cs, String oid) {
|
||||
if (!oid.startsWith("urn:oid:"))
|
||||
oid = "urn:oid:" + oid;
|
||||
|
@ -678,5 +669,18 @@ public class CodeSystemUtilities {
|
|||
public static boolean isNotCurrent(CodeSystem cs, ConceptDefinitionComponent c) {
|
||||
return isInactive(cs, c) || isDeprecated(cs, c, false);
|
||||
}
|
||||
|
||||
public static List<String> getDisplays(CodeSystem srcCS, ConceptDefinitionComponent cd) {
|
||||
List<String> list = new ArrayList<>();
|
||||
if (cd.hasDisplay()) {
|
||||
list.add(cd.getDisplay());
|
||||
}
|
||||
for (ConceptDefinitionDesignationComponent d : cd.getDesignation()) {
|
||||
if (!list.contains(d.getValue())) {
|
||||
list.add(d.getValue());
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,18 @@
|
|||
package org.hl7.fhir.r5.terminologies;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.r5.model.CanonicalType;
|
||||
import org.hl7.fhir.r5.model.CodeSystem;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.r5.model.ConceptMap;
|
||||
import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent;
|
||||
import org.hl7.fhir.r5.model.ConceptMap.SourceElementComponent;
|
||||
import org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent;
|
||||
import org.hl7.fhir.r5.model.Identifier;
|
||||
import org.hl7.fhir.r5.model.Meta;
|
||||
import org.hl7.fhir.r5.model.UriType;
|
||||
import org.hl7.fhir.r5.model.ValueSet;
|
||||
|
||||
public class ConceptMapUtilities {
|
||||
|
@ -30,5 +41,48 @@ public class ConceptMapUtilities {
|
|||
cm.addIdentifier().setSystem("urn:ietf:rfc:3986").setValue(oid);
|
||||
}
|
||||
|
||||
public static boolean hasMappingForSource(ConceptMap cm, String system, String version, String code) {
|
||||
for (ConceptMapGroupComponent grp : cm.getGroup()) {
|
||||
if (system.equals(grp.getSource())) { // to do: version
|
||||
for (SourceElementComponent e : grp.getElement()) {
|
||||
if (code.equals(e.getCode())) {
|
||||
return true; // doesn't matter if it's actually unmapped
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<Coding> listTargets(ConceptMap cm, List<String> systems) {
|
||||
List<Coding> list = new ArrayList<>();
|
||||
for (ConceptMapGroupComponent grp : cm.getGroup()) {
|
||||
if (systems.isEmpty() || systems.contains(grp.getSource())) { // to do: version
|
||||
for (SourceElementComponent e : grp.getElement()) {
|
||||
for (TargetElementComponent t : e.getTarget()) {
|
||||
if (t.hasCode()) {
|
||||
list.add(new Coding(grp.getTarget(), t.getCode(), t.getDisplay()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
public static ConceptMap makeShareable(ConceptMap cm) {
|
||||
if (!cm.hasExperimental()) {
|
||||
cm.setExperimental(false);
|
||||
}
|
||||
|
||||
if (!cm.hasMeta())
|
||||
cm.setMeta(new Meta());
|
||||
for (UriType t : cm.getMeta().getProfile())
|
||||
if ("http://hl7.org/fhir/StructureDefinition/shareableconceptmap".equals(t.getValue()))
|
||||
return cm;
|
||||
cm.getMeta().getProfile().add(new CanonicalType("http://hl7.org/fhir/StructureDefinition/shareableconceptmap"));
|
||||
return cm;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ import org.hl7.fhir.r5.model.ValueSet;
|
|||
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
|
||||
import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent;
|
||||
import org.hl7.fhir.r5.model.CodeType;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent;
|
||||
|
@ -311,5 +312,21 @@ public class ValueSetUtilities {
|
|||
}
|
||||
}
|
||||
|
||||
public static boolean hasCodeInExpansion(ValueSet vs, Coding code) {
|
||||
return hasCodeInExpansion(vs.getExpansion().getContains(), code);
|
||||
}
|
||||
|
||||
private static boolean hasCodeInExpansion(List<ValueSetExpansionContainsComponent> list, Coding code) {
|
||||
for (ValueSetExpansionContainsComponent c : list) {
|
||||
if (c.getSystem().equals(code.getSystem()) && c.getCode().equals(code.getCode())) {
|
||||
return true;
|
||||
}
|
||||
if (hasCodeInExpansion(c.getContains(), code)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -660,6 +660,9 @@ public class StructureMapUtilities {
|
|||
while (lexer.hasToken("imports"))
|
||||
parseImports(result, lexer);
|
||||
|
||||
while (lexer.hasToken("conceptmap"))
|
||||
parseConceptMap(result, lexer);
|
||||
|
||||
while (!lexer.done()) {
|
||||
parseGroup(result, lexer);
|
||||
}
|
||||
|
|
|
@ -527,6 +527,8 @@ public class I18nConstants {
|
|||
public static final String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH = "Type_Specific_Checks_DT_Primitive_Length";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_NOTEMPTY = "Type_Specific_Checks_DT_Primitive_NotEmpty";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX = "Type_Specific_Checks_DT_Primitive_Regex";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX_TYPE = "Type_Specific_Checks_DT_Primitive_Regex_Type";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX_EXCEPTION = "TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX_EXCEPTION";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_VALUEEXT = "Type_Specific_Checks_DT_Primitive_ValueExt";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_WS = "Type_Specific_Checks_DT_Primitive_WS";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_NO_ANNOTATIONS = "TYPE_SPECIFIC_CHECKS_DT_QTY_NO_ANNOTATIONS";
|
||||
|
@ -657,6 +659,10 @@ public class I18nConstants {
|
|||
public static final String CODESYSTEM_SHAREABLE_MISSING_HL7 = "CODESYSTEM_SHAREABLE_MISSING_HL7";
|
||||
public static final String CODESYSTEM_SHAREABLE_EXTRA_MISSING_HL7 = "CODESYSTEM_SHAREABLE_EXTRA_MISSING_HL7";
|
||||
public static final String CODESYSTEM_SHAREABLE_EXTRA_MISSING = "CODESYSTEM_SHAREABLE_EXTRA_MISSING";
|
||||
public static final String CONCEPTMAP_SHAREABLE_MISSING = "CONCEPTMAP_SHAREABLE_MISSING";
|
||||
public static final String CONCEPTMAP_SHAREABLE_MISSING_HL7 = "CONCEPTMAP_SHAREABLE_MISSING_HL7";
|
||||
public static final String CONCEPTMAP_SHAREABLE_EXTRA_MISSING_HL7 = "CONCEPTMAP_SHAREABLE_EXTRA_MISSING_HL7";
|
||||
public static final String CONCEPTMAP_SHAREABLE_EXTRA_MISSING = "CONCEPTMAP_SHAREABLE_EXTRA_MISSING";
|
||||
public static final String MEASURE_SHAREABLE_MISSING = "MEASURE_SHAREABLE_MISSING";
|
||||
public static final String MEASURE_SHAREABLE_MISSING_HL7 = "MEASURE_SHAREABLE_MISSING_HL7";
|
||||
public static final String MEASURE_SHAREABLE_EXTRA_MISSING_HL7 = "MEASURE_SHAREABLE_EXTRA_MISSING_HL7";
|
||||
|
@ -812,6 +818,32 @@ public class I18nConstants {
|
|||
public static final String SM_SOURCE_TYPE_NOT_FOUND = "SM_SOURCE_TYPE_NOT_FOUND";
|
||||
public static final String SM_TARGET_TYPE_NOT_FOUND = "SM_TARGET_TYPE_NOT_FOUND";
|
||||
public static final String SM_MATCHING_RULEGROUP_NOT_FOUND = "SM_MATCHING_RULEGROUP_NOT_FOUND";
|
||||
public static final String SM_TARGET_TRANSFORM_MISSING_PARAMS = "SM_TARGET_TRANSFORM_MISSING_PARAMS";
|
||||
public static final String SM_TARGET_TRANSFORM_TRANSLATE_NO_PARAM = "SM_TARGET_TRANSFORM_TRANSLATE_NO_PARAM";
|
||||
public static final String SM_TARGET_TRANSFORM_TRANSLATE_UNKNOWN_SOURCE = "SM_TARGET_TRANSFORM_TRANSLATE_UNKNOWN_SOURCE";
|
||||
public static final String SM_TARGET_TRANSFORM_TRANSLATE_CM_NOT_FOUND = "SM_TARGET_TRANSFORM_TRANSLATE_CM_NOT_FOUND";
|
||||
public static final String SM_TARGET_TRANSFORM_TRANSLATE_CM_BAD_MODE = "SM_TARGET_TRANSFORM_TRANSLATE_CM_BAD_MODE";
|
||||
public static final String SM_TARGET_TRANSLATE_BINDING_SOURCE = "SM_TARGET_TRANSLATE_BINDING_SOURCE";
|
||||
public static final String SM_TARGET_TRANSLATE_BINDING_VS_SOURCE = "SM_TARGET_TRANSLATE_BINDING_VS_SOURCE";
|
||||
public static final String SM_TARGET_TRANSLATE_BINDING_VSE_SOURCE = "SM_TARGET_TRANSLATE_BINDING_VSE_SOURCE";
|
||||
public static final String SM_TARGET_TRANSLATE_BINDING_SOURCE_UNMAPPED = "SM_TARGET_TRANSLATE_BINDING_SOURCE_UNMAPPED";
|
||||
public static final String SM_TARGET_TRANSLATE_BINDING_TARGET = "SM_TARGET_TRANSLATE_BINDING_TARGET";
|
||||
public static final String SM_TARGET_TRANSLATE_BINDING_VS_TARGET = "SM_TARGET_TRANSLATE_BINDING_VS_TARGET";
|
||||
public static final String SM_TARGET_TRANSLATE_BINDING_VSE_TARGET = "SM_TARGET_TRANSLATE_BINDING_VSE_TARGET";
|
||||
public static final String SM_TARGET_TRANSLATE_BINDING_TARGET_WRONG = "SM_TARGET_TRANSLATE_BINDING_TARGET_WRONG";
|
||||
public static final String CONCEPTMAP_GROUP_SOURCE_MISSING = "CONCEPTMAP_GROUP_SOURCE_MISSING";
|
||||
public static final String CONCEPTMAP_GROUP_SOURCE_UNKNOWN = "CONCEPTMAP_GROUP_SOURCE_UNKNOWN";
|
||||
public static final String CONCEPTMAP_GROUP_TARGET_MISSING = "CONCEPTMAP_GROUP_TARGET_MISSING";
|
||||
public static final String CONCEPTMAP_GROUP_TARGET_UNKNOWN = "CONCEPTMAP_GROUP_TARGET_UNKNOWN";
|
||||
public static final String CONCEPTMAP_GROUP_SOURCE_CODE_INVALID = "CONCEPTMAP_GROUP_SOURCE_CODE_INVALID";
|
||||
public static final String CONCEPTMAP_GROUP_SOURCE_DISPLAY_INVALID = "CONCEPTMAP_GROUP_SOURCE_DISPLAY_INVALID";
|
||||
public static final String CONCEPTMAP_GROUP_TARGET_CODE_INVALID = "CONCEPTMAP_GROUP_TARGET_CODE_INVALID";
|
||||
public static final String CONCEPTMAP_GROUP_TARGET_DISPLAY_INVALID = "CONCEPTMAP_GROUP_TARGET_DISPLAY_INVALID";
|
||||
public static final String CONCEPTMAP_GROUP_TARGET_PROPERTY_INVALID = "CONCEPTMAP_GROUP_TARGET_PROPERTY_INVALID";
|
||||
public static final String CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_MISMATCH = "CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_MISMATCH";
|
||||
public static final String CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_NO_SYSTEM = "CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_NO_SYSTEM";
|
||||
public static final String CONCEPTMAP_GROUP_TARGET_PROPERTY_CODE_INVALID = "CONCEPTMAP_GROUP_TARGET_PROPERTY_CODE_INVALID";
|
||||
public static final String CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_UNKNOWN_SYSTEM = "CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_UNKNOWN_SYSTEM";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ public class PackageHacker {
|
|||
private static boolean useSecureReferences = false;
|
||||
|
||||
public static void main(String[] args) throws FileNotFoundException, IOException {
|
||||
new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/5.0.0-draft-final/hl7.fhir.r5.search.tgz");
|
||||
new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/us/vitals/2020Sep/package.tgz");
|
||||
}
|
||||
|
||||
private void edit(String name) throws FileNotFoundException, IOException {
|
||||
|
@ -42,15 +42,7 @@ public class PackageHacker {
|
|||
System.out.println(nice(pck.getNpm()));
|
||||
|
||||
change(pck.getNpm());
|
||||
// fixContent(pck.getFolders().get("package").getContent());
|
||||
// if (pck.getFolders().containsKey("openapi")) {
|
||||
// fixContent(pck.getFolders().get("openapi").getContent());
|
||||
// }
|
||||
// if (pck.getFolders().containsKey("xml")) {
|
||||
// fixContent(pck.getFolders().get("xml").getContent());
|
||||
// }
|
||||
// fixExampleContent(pck.getFolders().get("example").getContent());
|
||||
|
||||
|
||||
System.out.println("Revised Package");
|
||||
System.out.println("=======================");
|
||||
System.out.println(nice(pck.getNpm()));
|
||||
|
@ -79,35 +71,9 @@ public class PackageHacker {
|
|||
}
|
||||
|
||||
private void change(JsonObject npm) throws FileNotFoundException, IOException {
|
||||
// fixVersions(npm);
|
||||
// fixVersions(npm, ver);
|
||||
npm.remove("notForPublication");
|
||||
// npm.remove("name");
|
||||
// npm.add("name", "hl7.fhir.r5.search");
|
||||
// npm.remove("title");
|
||||
// npm.add("title", "FHIR 5.0.0-draft-final package : Server Search Parameters");
|
||||
// npm.add("url", "https://hl7chile.cl/fhir/ig/CoreCL/1.7.0");
|
||||
// npm.remove("name");
|
||||
// npm.addProperty("name", "hl7.fhir.uv.smart-app-launch");
|
||||
// npm.remove("canonical");
|
||||
// npm.addProperty("canonical", "http://hl7.org/fhir/us/davinci-drug-formulary");
|
||||
//// npm.remove("description");
|
||||
//// npm.addProperty("description", "Group Wrapper that includes all the R4 packages");
|
||||
// npm.remove("url");
|
||||
// npm.addProperty("url", "http://hl7.org/fhir/R4B");
|
||||
// npm.remove("homepage");
|
||||
// npm.addProperty("homepage", "http://hl7.org/fhir/R4B");
|
||||
// npm.remove("dependencies");
|
||||
// JsonObject dep = new JsonObject();
|
||||
// npm.add("dependencies", dep);
|
||||
// dep.addProperty("hl7.fhir.r4.core", "4.0.1");
|
||||
// dep.addProperty("ch.fhir.ig.ch-core", "2.0.0");
|
||||
// dep.addProperty("ch.fhir.ig.ch-epr-term", "2.0.4");
|
||||
// dep.addProperty("ch.fhir.ig.ch-emed","current");
|
||||
|
||||
// dep.addProperty("hl7.fhir.r4.examples", "4.0.1");
|
||||
// dep.addProperty("hl7.fhir.r4.expansions", "4.0.1");
|
||||
// dep.addProperty("hl7.fhir.r4.elements", "4.0.1");
|
||||
// npm.addProperty("jurisdiction", "urn:iso:std:iso:3166#CL");
|
||||
npm.set("name", "hl7.fhir.us.vitals");
|
||||
}
|
||||
|
||||
private void fixVersionInContent(Map<String, byte[]> content) {
|
||||
|
|
|
@ -212,6 +212,8 @@ Type_Specific_Checks_DT_OID_Valid = OIDs must be valid ({0})
|
|||
Type_Specific_Checks_DT_Primitive_Length = value is longer than permitted maximum length of {0}
|
||||
Type_Specific_Checks_DT_Primitive_NotEmpty = value cannot be empty
|
||||
Type_Specific_Checks_DT_Primitive_Regex = Element value ''{0}'' does not meet regex ''{1}''
|
||||
TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX_EXCEPTION = "Exception evaluating regex ''{0}'' on type {1}: {2}
|
||||
Type_Specific_Checks_DT_Primitive_Regex_Type = Element value ''{0}'' does not meet {1} regex ''{2}''
|
||||
Type_Specific_Checks_DT_Primitive_ValueExt = Primitive types must have a value or must have child extensions
|
||||
Type_Specific_Checks_DT_Primitive_WS = Primitive types should not only be whitespace
|
||||
Type_Specific_Checks_DT_String_Length = value is longer than permitted maximum length of 1 MB (1048576 bytes)
|
||||
|
@ -776,6 +778,10 @@ CODESYSTEM_SHAREABLE_MISSING = The ShareableCodeSystem profile says that the {0}
|
|||
CODESYSTEM_SHAREABLE_EXTRA_MISSING = The ShareableCodeSystem profile recommends that the {0} element is populated, but it is not present. Published code systems SHOULD conform to the ShareableCodeSystem profile
|
||||
CODESYSTEM_SHAREABLE_MISSING_HL7 = The ShareableCodeSystem profile says that the {0} element is mandatory, but it is not found. HL7 Published code systems SHALL conform to the ShareableCodeSystem profile
|
||||
CODESYSTEM_SHAREABLE_EXTRA_MISSING_HL7 = The ShareableCodeSystem profile recommends that the {0} element is populated, but it is not found. HL7 Published code systems SHALL conform to the ShareableCodeSystem profile
|
||||
CONCEPTMAP_SHAREABLE_MISSING = The ShareableConceptMap profile says that the {0} element is mandatory, but it is not present. Published concept maps SHOULD conform to the ShareableConceptMap profile
|
||||
CONCEPTMAP_SHAREABLE_EXTRA_MISSING = The ShareableConceptMap profile recommends that the {0} element is populated, but it is not present. Published concept maps SHOULD conform to the ShareableConceptMap profile
|
||||
CONCEPTMAP_SHAREABLE_MISSING_HL7 = The ShareableConceptMap profile says that the {0} element is mandatory, but it is not found. HL7 Published concept maps SHALL conform to the ShareableConceptMap profile
|
||||
CONCEPTMAP_SHAREABLE_EXTRA_MISSING_HL7 = The ShareableConceptMap profile recommends that the {0} element is populated, but it is not found. HL7 Published concept maps SHALL conform to the ShareableConceptMap profile
|
||||
MEASURE_SHAREABLE_MISSING = The ShareableMeasure profile says that the {0} element is mandatory, but it is not present. Published measures SHOULD conform to the ShareableMeasure profile
|
||||
MEASURE_SHAREABLE_EXTRA_MISSING = The ShareableMeasure profile recommends that the {0} element is populated, but it is not present. Published measures SHOULD conform to the ShareableMeasure profile
|
||||
MEASURE_SHAREABLE_MISSING_HL7 = The ShareableMeasure profile says that the {0} element is mandatory, but it is not found. HL7 Published measures SHALL conform to the ShareableMeasure profile
|
||||
|
@ -849,7 +855,7 @@ SM_TARGET_PATH_MULTIPLE_MATCHES = The target path {0}.{1} refers to the path {2}
|
|||
SM_SOURCE_TYPE_INVALID = The type {0} is not valid in this source context {1}. The possible types are [{2}]
|
||||
SM_TARGET_TRANSFORM_PARAM_COUNT_RANGE = Transform {0} takes {1}-{2} parameter(s) but {3} were found
|
||||
SM_TARGET_TRANSFORM_PARAM_COUNT_SINGLE = Transform {0} takes {1} parameter(s) but {2} were found
|
||||
SM_TARGET_TRANSFORM_NOT_CHECKED = Transform {0} not checked yet
|
||||
SM_TARGET_TRANSFORM_NOT_CHECKED = Transform {0} not checked dyet
|
||||
SM_TARGET_NO_TRANSFORM_NO_CHECKED = When there is no transform, parameters can''t be provided
|
||||
SM_TARGET_TRANSFORM_TYPE_UNPROCESSIBLE = The value of the type parameter could not be processed
|
||||
SM_TARGET_TRANSFORM_PARAM_UNPROCESSIBLE = The parameter at index {0} could not be processed (type = {1})
|
||||
|
@ -862,7 +868,31 @@ SM_ORPHAN_GROUP = This group is not called from within this mapping script, and
|
|||
SM_SOURCE_TYPE_NOT_FOUND = No source type was found, so the default group for this implied dependent rule could not be determined
|
||||
SM_TARGET_TYPE_NOT_FOUND = No target type was found, so the default group for this implied dependent rule could not be determined
|
||||
SM_MATCHING_RULEGROUP_NOT_FOUND = Unable to find a default rule for the type pair source={0} and target={1}
|
||||
|
||||
|
||||
SM_TARGET_TRANSFORM_MISSING_PARAMS = One or more parameters to the translate operation are missing (should be 3, was {0})
|
||||
SM_TARGET_TRANSFORM_TRANSLATE_NO_PARAM = No value for the {0} parameter found
|
||||
SM_TARGET_TRANSFORM_TRANSLATE_UNKNOWN_SOURCE = The source variable {0} is unknown
|
||||
SM_TARGET_TRANSFORM_TRANSLATE_CM_NOT_FOUND = The map_uri ''{0}'' could not be resolved, so the map can't be checked
|
||||
SM_TARGET_TRANSFORM_TRANSLATE_CM_BAD_MODE = The value ''{0}'' for the output parameter is incorrect
|
||||
SM_TARGET_TRANSLATE_BINDING_SOURCE = The source variable does not have a required binding, so this concept map can''t be checked
|
||||
SM_TARGET_TRANSLATE_BINDING_VS_SOURCE = The source variable refers to an unknown value set ''{0}'', so this concept map can''t be checked
|
||||
SM_TARGET_TRANSLATE_BINDING_VSE_SOURCE = There was an error expanding the source value set, so this concept map can''t be checked: ''{0}''
|
||||
SM_TARGET_TRANSLATE_BINDING_SOURCE_UNMAPPED = The source value set includes one or more codes that the map does not translate: {0}
|
||||
SM_TARGET_TRANSLATE_BINDING_TARGET = The target variable does not have a required binding, so this concept map can''t be checked
|
||||
SM_TARGET_TRANSLATE_BINDING_VS_TARGET = The target variable refers to an unknown value set ''{0}'', so this concept map can''t be checked
|
||||
SM_TARGET_TRANSLATE_BINDING_VSE_TARGET = There was an error expanding the target value set, so this concept map can''t be checked: ''{0}''
|
||||
SM_TARGET_TRANSLATE_BINDING_TARGET_WRONG = The map produces one or more codes that the target value set does not include: {0}
|
||||
CONCEPTMAP_GROUP_SOURCE_MISSING = No Source Code System, so the source codes cannot be checked
|
||||
CONCEPTMAP_GROUP_SOURCE_UNKNOWN = Unknown Source Code System, so the source codes cannot be checked
|
||||
CONCEPTMAP_GROUP_TARGET_MISSING = No Target Code System, so the source codes cannot be checked
|
||||
CONCEPTMAP_GROUP_TARGET_UNKNOWN = Unknown Target Code System, so the source codes cannot be checked
|
||||
CONCEPTMAP_GROUP_SOURCE_CODE_INVALID = The source code ''{0}'' is not valid in the code system {1}
|
||||
CONCEPTMAP_GROUP_SOURCE_DISPLAY_INVALID = The source display ''{0}'' is not valid. Possible codes {1}
|
||||
CONCEPTMAP_GROUP_TARGET_CODE_INVALID =The target code ''{0}'' is not valid in the code system {1}
|
||||
CONCEPTMAP_GROUP_TARGET_DISPLAY_INVALID = The target display ''{0}'' is not valid. Possible codes {1}
|
||||
CONCEPTMAP_GROUP_TARGET_PROPERTY_INVALID = The property code ''{0}'' is not known
|
||||
CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_MISMATCH = The type of this property should be {1} not {0}
|
||||
CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_NO_SYSTEM = Since no system has been provided, a plain code cannot be used
|
||||
CONCEPTMAP_GROUP_TARGET_PROPERTY_CODE_INVALID = The code {0} is invalid in the system {1}
|
||||
CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_UNKNOWN_SYSTEM = The system {0} is unknown, so code values can''t be checked
|
||||
|
||||
|
|
@ -185,6 +185,7 @@ import org.hl7.fhir.validation.cli.model.HtmlInMarkdownCheck;
|
|||
import org.hl7.fhir.validation.cli.utils.QuestionnaireMode;
|
||||
import org.hl7.fhir.validation.instance.type.BundleValidator;
|
||||
import org.hl7.fhir.validation.instance.type.CodeSystemValidator;
|
||||
import org.hl7.fhir.validation.instance.type.ConceptMapValidator;
|
||||
import org.hl7.fhir.validation.instance.type.MeasureValidator;
|
||||
import org.hl7.fhir.validation.instance.type.QuestionnaireValidator;
|
||||
import org.hl7.fhir.validation.instance.type.SearchParameterValidator;
|
||||
|
@ -2288,6 +2289,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
if (regex != null) {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches(regex), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX, e.primitiveValue(), regex) && ok;
|
||||
}
|
||||
|
||||
if (!"xhtml".equals(type)) {
|
||||
if (securityChecks) {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, !containsHtmlTags(e.primitiveValue()), I18nConstants.SECURITY_STRING_CONTENT_ERROR) && ok;
|
||||
|
@ -2304,9 +2306,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
String url = e.primitiveValue();
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("oid:"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URI_OID) && ok;
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("uuid:"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URI_UUID) && ok;
|
||||
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, url.equals(url.trim().replace(" ", ""))
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, url.equals(url.trim().replace(" ", ""))
|
||||
// work around an old invalid example in a core package
|
||||
|| "http://www.acme.com/identifiers/patient or urn:ietf:rfc:3986 if the Identifier.value itself is a full uri".equals(url), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URI_WS, url);
|
||||
|| "http://www.acme.com/identifiers/patient or urn:ietf:rfc:3986 if the Identifier.value itself is a full uri".equals(url), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URI_WS, url) && ok;
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || url.length() <= context.getMaxLength(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH, context.getMaxLength()) && ok;
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH, context.getMaxLength()) && ok;
|
||||
|
||||
|
@ -2429,6 +2431,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, v >= 0, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_INTEGER_LT0) && ok;
|
||||
if (type.equals("positiveInt"))
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, v > 0, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_INTEGER_LT1) && ok;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
if (type.equals("integer64")) {
|
||||
|
@ -2540,11 +2544,47 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
if (context.hasPattern()) {
|
||||
ok = checkFixedValue(errors, path, e, context.getPattern(), profile.getVersionedUrl(), context.getSliceName(), null, true) && ok;
|
||||
}
|
||||
|
||||
|
||||
if (ok && !Utilities.existsInList(e.fhirType(), "id", "base64Binary", "markdown")) { // ids get checked elsewhere
|
||||
String regext = FHIRPathExpressionFixer.fixRegex(getRegexFromType(e.fhirType()));
|
||||
if (regext != null) {
|
||||
try {
|
||||
boolean matches = e.primitiveValue().matches(regext);
|
||||
if (!matches) {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, matches, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX_TYPE, e.primitiveValue(), e.fhirType(), regext) && ok;
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX_EXCEPTION, regext, e.fhirType(), ex.getMessage()) && ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for nothing to check
|
||||
return ok;
|
||||
}
|
||||
|
||||
private String getRegexFromType(String fhirType) {
|
||||
StructureDefinition sd = context.fetchTypeDefinition(fhirType);
|
||||
if (sd != null) {
|
||||
for (ElementDefinition ed : sd.getSnapshot().getElement()) {
|
||||
if (ed.getPath().endsWith(".value")) {
|
||||
String regex = ed.getExtensionString(ToolingExtensions.EXT_REGEX);
|
||||
if (regex != null) {
|
||||
return regex;
|
||||
}
|
||||
for (TypeRefComponent td : ed.getType()) {
|
||||
regex = td.getExtensionString(ToolingExtensions.EXT_REGEX);
|
||||
if (regex != null) {
|
||||
return regex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean checkTypeValue(List<ValidationMessage> errors, String path, Element e, Element sd) {
|
||||
String v = e.primitiveValue();
|
||||
if (v == null) {
|
||||
|
@ -3478,14 +3518,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
StructureDefinition sd = context.fetchResource(StructureDefinition.class, tp.getValue(), profile);
|
||||
if (sd != null) {
|
||||
types.add(sd.getType());
|
||||
}
|
||||
StructureDefinition sdF = sdFT;
|
||||
while (sdF != null) {
|
||||
if (sdF.getType().equals(sd.getType())) {
|
||||
rok = true;
|
||||
break;
|
||||
StructureDefinition sdF = sdFT;
|
||||
while (sdF != null) {
|
||||
if (sdF.getType().equals(sd.getType())) {
|
||||
rok = true;
|
||||
break;
|
||||
}
|
||||
sdF = sdF.hasBaseDefinition() ? context.fetchResource(StructureDefinition.class, sdF.getBaseDefinition(), sdF) : null;
|
||||
}
|
||||
sdF = sdF.hasBaseDefinition() ? context.fetchResource(StructureDefinition.class, sdF.getBaseDefinition(), sdF) : null;
|
||||
}
|
||||
}
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), path, types.isEmpty() || rok,
|
||||
|
@ -4008,7 +4048,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return false;
|
||||
else
|
||||
lastWasSpace = true;
|
||||
} else if (Character.isWhitespace(c))
|
||||
} else if (Character.isWhitespace(c) || c == '\u00A0')
|
||||
return false;
|
||||
else
|
||||
lastWasSpace = false;
|
||||
|
@ -4997,6 +5037,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return validateCapabilityStatement(errors, element, stack);
|
||||
} else if (element.getType().equals("CodeSystem")) {
|
||||
return new CodeSystemValidator(context, timeTracker, this, xverManager, jurisdiction).validateCodeSystem(errors, element, stack, baseOptions.setLanguage(stack.getWorkingLang()));
|
||||
} else if (element.getType().equals("ConceptMap")) {
|
||||
return new ConceptMapValidator(context, timeTracker, this, xverManager, jurisdiction).validateConceptMap(errors, element, stack, baseOptions.setLanguage(stack.getWorkingLang()));
|
||||
} else if (element.getType().equals("SearchParameter")) {
|
||||
return new SearchParameterValidator(context, timeTracker, fpe, xverManager, jurisdiction).validateSearchParameter(errors, element, stack);
|
||||
} else if (element.getType().equals("StructureDefinition")) {
|
||||
|
|
|
@ -0,0 +1,270 @@
|
|||
package org.hl7.fhir.validation.instance.type;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.elementmodel.Element;
|
||||
import org.hl7.fhir.r5.model.CodeSystem;
|
||||
import org.hl7.fhir.r5.model.CodeSystem.CodeSystemContentMode;
|
||||
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.r5.model.ValueSet;
|
||||
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
|
||||
import org.hl7.fhir.r5.utils.XVerExtensionManager;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.VersionUtilities;
|
||||
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
|
||||
import org.hl7.fhir.utilities.validation.ValidationOptions;
|
||||
import org.hl7.fhir.validation.BaseValidator;
|
||||
import org.hl7.fhir.validation.TimeTracker;
|
||||
import org.hl7.fhir.validation.instance.InstanceValidator;
|
||||
import org.hl7.fhir.validation.instance.type.ConceptMapValidator.PropertyDefinition;
|
||||
import org.hl7.fhir.validation.instance.utils.NodeStack;
|
||||
|
||||
public class ConceptMapValidator extends BaseValidator {
|
||||
|
||||
public class PropertyDefinition {
|
||||
private String type;
|
||||
private String system;
|
||||
private CodeSystem cs;
|
||||
protected PropertyDefinition(String type, String system, CodeSystem cs) {
|
||||
super();
|
||||
this.type = type;
|
||||
this.system = system;
|
||||
this.cs = cs;
|
||||
}
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public String getSystem() {
|
||||
return system;
|
||||
}
|
||||
public CodeSystem getCs() {
|
||||
return cs;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private InstanceValidator parent;
|
||||
|
||||
public ConceptMapValidator(IWorkerContext context, TimeTracker timeTracker, InstanceValidator parent, XVerExtensionManager xverManager, Coding jurisdiction) {
|
||||
super(context, xverManager);
|
||||
source = Source.InstanceValidator;
|
||||
this.timeTracker = timeTracker;
|
||||
this.jurisdiction = jurisdiction;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public boolean validateConceptMap(List<ValidationMessage> errors, Element cm, NodeStack stack, ValidationOptions options) {
|
||||
boolean ok = true;
|
||||
Map<String, PropertyDefinition> props = new HashMap<>();
|
||||
Map<String, String> attribs = new HashMap<>();
|
||||
|
||||
if (VersionUtilities.isR5Plus(context.getVersion())) {
|
||||
List<Element> properties = cm.getChildrenByName("property");
|
||||
int ci = 0;
|
||||
for (Element property : properties) {
|
||||
String code = property.getChildValue("code");
|
||||
String type = property.getChildValue("type");
|
||||
String system = property.getChildValue("system");
|
||||
CodeSystem cs = system != null ? context.fetchCodeSystem(system) : null;
|
||||
ok = rule(errors, "2023-03-05", IssueType.REQUIRED, property.line(), property.col(), stack.push(property, ci, null, null).getLiteralPath(),
|
||||
!"code".equals(type) || system != null, I18nConstants.CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_NO_SYSTEM) && ok;
|
||||
warning(errors, "2023-03-05", IssueType.REQUIRED, property.line(), property.col(), stack.push(property, ci, null, null).getLiteralPath(),
|
||||
system == null || cs != null, I18nConstants.CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_UNKNOWN_SYSTEM, system);
|
||||
if (code != null) {
|
||||
props.put(code, new PropertyDefinition(type, system, cs));
|
||||
}
|
||||
ci++;
|
||||
}
|
||||
|
||||
List<Element> attributes = cm.getChildrenByName("additionalAttribute");
|
||||
for (Element attribute : attributes) {
|
||||
String code = attribute.getChildValue("code");
|
||||
String type = attribute.getChildValue("type");
|
||||
if (code != null) {
|
||||
attribs.put(code, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
List<Element> groups = cm.getChildrenByName("group");
|
||||
int ci = 0;
|
||||
for (Element group : groups) {
|
||||
ok = validateGroup(errors, group, stack.push(group, ci, null, null), props, attribs) && ok;
|
||||
ci++;
|
||||
}
|
||||
|
||||
if (!stack.isContained()) {
|
||||
ok = checkShareableConceptMap(errors, cm, stack) && ok;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
private boolean validateGroup(List<ValidationMessage> errors, Element grp, NodeStack stack, Map<String, PropertyDefinition> props, Map<String, String> attribs) {
|
||||
boolean ok = true;
|
||||
CodeSystem srcCS = null;
|
||||
CodeSystem tgtCS = null;
|
||||
Element e = grp.getNamedChild("source");
|
||||
if (warning(errors, "2023-03-05", IssueType.REQUIRED, grp.line(), grp.col(), stack.getLiteralPath(), e != null, I18nConstants.CONCEPTMAP_GROUP_SOURCE_MISSING)) {
|
||||
srcCS = context.fetchCodeSystem(e.getValue());
|
||||
warning(errors, "2023-03-05", IssueType.NOTFOUND, grp.line(), grp.col(), stack.push(e, -1, null, null).getLiteralPath(), srcCS != null, I18nConstants.CONCEPTMAP_GROUP_SOURCE_UNKNOWN, e.getValue());
|
||||
}
|
||||
e = grp.getNamedChild("target");
|
||||
if (warning(errors, "2023-03-05", IssueType.REQUIRED, grp.line(), grp.col(), stack.getLiteralPath(), e != null, I18nConstants.CONCEPTMAP_GROUP_TARGET_MISSING)) {
|
||||
tgtCS = context.fetchCodeSystem(e.getValue());
|
||||
warning(errors, "2023-03-05", IssueType.NOTFOUND, grp.line(), grp.col(), stack.push(e, -1, null, null).getLiteralPath(), tgtCS != null, I18nConstants.CONCEPTMAP_GROUP_TARGET_UNKNOWN, e.getValue());
|
||||
}
|
||||
List<Element> elements = grp.getChildrenByName("element");
|
||||
int ci = 0;
|
||||
for (Element element : elements) {
|
||||
ok = validateGroupElement(errors, element, stack.push(element, ci, null, null), srcCS, tgtCS, props, attribs) && ok;
|
||||
ci++;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private boolean validateGroupElement(List<ValidationMessage> errors, Element src, NodeStack stack, CodeSystem srcCS, CodeSystem tgtCS, Map<String, PropertyDefinition> props, Map<String, String> attribs) {
|
||||
boolean ok = true;
|
||||
|
||||
Element code = src.getNamedChild("code");
|
||||
if (code != null && srcCS != null) {
|
||||
String c = code.getValue();
|
||||
ConceptDefinitionComponent cd = CodeSystemUtilities.getCode(srcCS, c);
|
||||
if (warningOrError(srcCS.getContent() == CodeSystemContentMode.COMPLETE, errors, "2023-03-05", IssueType.REQUIRED, code.line(), code.col(), stack.push(code, -1, null, null).getLiteralPath(), cd != null, I18nConstants.CONCEPTMAP_GROUP_SOURCE_CODE_INVALID, c, srcCS.getVersionedUrl())) {
|
||||
Element display = src.getNamedChild("display");
|
||||
if (display != null) {
|
||||
List<String> displays = CodeSystemUtilities.getDisplays(srcCS, cd);
|
||||
ok = rule(errors, "2023-03-05", IssueType.REQUIRED, code.line(), code.col(), stack.push(code, -1, null, null).getLiteralPath(), displays.contains(display.getValue()), I18nConstants.CONCEPTMAP_GROUP_SOURCE_DISPLAY_INVALID, display.getValue(), displays) && ok;
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
List<Element> targets = src.getChildrenByName("target");
|
||||
int ci = 0;
|
||||
for (Element target : targets) {
|
||||
ok = validateGroupElementTarget(errors, target, stack.push(target, ci, null, null), srcCS, tgtCS, props, attribs) && ok;
|
||||
ci++;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private boolean validateGroupElementTarget(List<ValidationMessage> errors, Element tgt, NodeStack stack, CodeSystem srcCS, CodeSystem tgtCS, Map<String, PropertyDefinition> props, Map<String, String> attribs) {
|
||||
boolean ok = true;
|
||||
|
||||
Element code = tgt.getNamedChild("code");
|
||||
if (code != null && tgtCS != null) {
|
||||
String c = code.getValue();
|
||||
ConceptDefinitionComponent cd = CodeSystemUtilities.getCode(tgtCS, c);
|
||||
if (warningOrError(tgtCS.getContent() == CodeSystemContentMode.COMPLETE, errors, "2023-03-05", IssueType.REQUIRED, code.line(), code.col(), stack.push(code, -1, null, null).getLiteralPath(), cd != null, I18nConstants.CONCEPTMAP_GROUP_TARGET_CODE_INVALID, c, tgtCS.getVersionedUrl())) {
|
||||
Element display = tgt.getNamedChild("display");
|
||||
if (display != null) {
|
||||
List<String> displays = CodeSystemUtilities.getDisplays(tgtCS, cd);
|
||||
ok = rule(errors, "2023-03-05", IssueType.REQUIRED, code.line(), code.col(), stack.push(code, -1, null, null).getLiteralPath(), displays.contains(display.getValue()), I18nConstants.CONCEPTMAP_GROUP_TARGET_DISPLAY_INVALID, display.getValue(), displays) && ok;
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (VersionUtilities.isR5Plus(context.getVersion())) {
|
||||
List<Element> properties = tgt.getChildrenByName("property");
|
||||
int ci = 0;
|
||||
for (Element property : properties) {
|
||||
ok = validateGroupElementTargetProperty(errors, property, stack.push(property, ci, null, null), props) && ok;
|
||||
ci++;
|
||||
}
|
||||
|
||||
List<Element> attributes = tgt.getChildrenByName("dependsOn");
|
||||
ci = 0;
|
||||
for (Element attribute : attributes) {
|
||||
ok = validateGroupElementTargetAttribute(errors, attribute, stack.push(attribute, ci, null, null), attribs) && ok;
|
||||
ci++;
|
||||
}
|
||||
attributes = tgt.getChildrenByName("product");
|
||||
ci = 0;
|
||||
for (Element attribute : attributes) {
|
||||
ok = validateGroupElementTargetAttribute(errors, attribute, stack.push(attribute, ci, null, null), attribs) && ok;
|
||||
ci++;
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private boolean validateGroupElementTargetProperty(List<ValidationMessage> errors, Element property, NodeStack stack, Map<String, PropertyDefinition> props) {
|
||||
boolean ok = true;
|
||||
Element codeE = property.getNamedChild("code");
|
||||
Element valueE = property.getNamedChild("value");
|
||||
String code = codeE.getValue();
|
||||
if (rule(errors, "2023-03-05", IssueType.REQUIRED, codeE.line(), codeE.col(), stack.push(codeE, -1, null, null).getLiteralPath(), props.containsKey(code), I18nConstants.CONCEPTMAP_GROUP_TARGET_PROPERTY_INVALID, code, props.keySet())) {
|
||||
PropertyDefinition defn = props.get(code);
|
||||
NodeStack stackV = stack.push(valueE, -1, null, null);
|
||||
if (rule(errors, "2023-03-05", IssueType.REQUIRED, codeE.line(), codeE.col(), stackV.getLiteralPath(), valueE.fhirType().equals(defn.getType()), I18nConstants.CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_MISMATCH, valueE.fhirType(), defn.getType())) {
|
||||
if (valueE.fhirType().equals("code")) {
|
||||
if (defn.getCs() != null) {
|
||||
ok = rule(errors, "2023-03-05", IssueType.REQUIRED, codeE.line(), codeE.col(), stackV.getLiteralPath(),
|
||||
CodeSystemUtilities.findCode(defn.getCs().getConcept(), valueE.getValue()) != null, I18nConstants.CONCEPTMAP_GROUP_TARGET_PROPERTY_CODE_INVALID, valueE.getValue(), defn.getCs().getVersionedUrl()) && ok;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private boolean validateGroupElementTargetAttribute(List<ValidationMessage> errors, Element attribute, NodeStack stack, Map<String, String> attribs) {
|
||||
boolean ok = true;
|
||||
Element codeE = attribute.getNamedChild("attribute");
|
||||
Element valueE = attribute.getNamedChild("value");
|
||||
String code = codeE.getValue();
|
||||
if (rule(errors, "2023-03-05", IssueType.REQUIRED, codeE.line(), codeE.col(), stack.push(codeE, -1, null, null).getLiteralPath(), attribs.containsKey(code), I18nConstants.CONCEPTMAP_GROUP_TARGET_PROPERTY_INVALID, code, attribs.keySet())) {
|
||||
NodeStack stackV = stack.push(valueE, -1, null, null);
|
||||
ok = rule(errors, "2023-03-05", IssueType.REQUIRED, codeE.line(), codeE.col(), stackV.getLiteralPath(), valueE.fhirType().equals(attribs.get(code)), I18nConstants.CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_MISMATCH, valueE.fhirType(), attribs.get(code)) && ok;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private boolean checkShareableConceptMap(List<ValidationMessage> errors, Element cs, NodeStack stack) {
|
||||
if (parent.isForPublication()) {
|
||||
if (isHL7(cs)) {
|
||||
boolean ok = true;
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.REQUIRED, cs.line(), cs.col(), stack.getLiteralPath(), cs.hasChild("url"), I18nConstants.CONCEPTMAP_SHAREABLE_MISSING_HL7, "url") && ok;
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.REQUIRED, cs.line(), cs.col(), stack.getLiteralPath(), cs.hasChild("version"), I18nConstants.CONCEPTMAP_SHAREABLE_MISSING_HL7, "version") && ok;
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.REQUIRED, cs.line(), cs.col(), stack.getLiteralPath(), cs.hasChild("title"), I18nConstants.CONCEPTMAP_SHAREABLE_MISSING_HL7, "title") && ok;
|
||||
warning(errors, NO_RULE_DATE, IssueType.REQUIRED, cs.line(), cs.col(), stack.getLiteralPath(), cs.hasChild("name"), I18nConstants.CONCEPTMAP_SHAREABLE_EXTRA_MISSING_HL7, "name");
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.REQUIRED, cs.line(), cs.col(), stack.getLiteralPath(), cs.hasChild("status"), I18nConstants.CONCEPTMAP_SHAREABLE_MISSING_HL7, "status") && ok;
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.REQUIRED, cs.line(), cs.col(), stack.getLiteralPath(), cs.hasChild("experimental"), I18nConstants.CONCEPTMAP_SHAREABLE_MISSING_HL7, "experimental") && ok;
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.REQUIRED, cs.line(), cs.col(), stack.getLiteralPath(), cs.hasChild("description"), I18nConstants.CONCEPTMAP_SHAREABLE_MISSING_HL7, "description") && ok;
|
||||
return ok;
|
||||
} else {
|
||||
warning(errors, NO_RULE_DATE, IssueType.REQUIRED, cs.line(), cs.col(), stack.getLiteralPath(), cs.hasChild("url"), I18nConstants.CONCEPTMAP_SHAREABLE_MISSING, "url");
|
||||
warning(errors, NO_RULE_DATE, IssueType.REQUIRED, cs.line(), cs.col(), stack.getLiteralPath(), cs.hasChild("version"), I18nConstants.CONCEPTMAP_SHAREABLE_MISSING, "version");
|
||||
warning(errors, NO_RULE_DATE, IssueType.REQUIRED, cs.line(), cs.col(), stack.getLiteralPath(), cs.hasChild("title"), I18nConstants.CONCEPTMAP_SHAREABLE_MISSING, "title");
|
||||
warning(errors, NO_RULE_DATE, IssueType.REQUIRED, cs.line(), cs.col(), stack.getLiteralPath(), cs.hasChild("name"), I18nConstants.CONCEPTMAP_SHAREABLE_EXTRA_MISSING, "name");
|
||||
warning(errors, NO_RULE_DATE, IssueType.REQUIRED, cs.line(), cs.col(), stack.getLiteralPath(), cs.hasChild("status"), I18nConstants.CONCEPTMAP_SHAREABLE_MISSING, "status");
|
||||
warning(errors, NO_RULE_DATE, IssueType.REQUIRED, cs.line(), cs.col(), stack.getLiteralPath(), cs.hasChild("experimental"), I18nConstants.CONCEPTMAP_SHAREABLE_MISSING, "experimental");
|
||||
warning(errors, NO_RULE_DATE, IssueType.REQUIRED, cs.line(), cs.col(), stack.getLiteralPath(), cs.hasChild("description"), I18nConstants.CONCEPTMAP_SHAREABLE_MISSING, "description");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -4,13 +4,18 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
|
||||
import org.hl7.fhir.r5.context.ContextUtilities;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.elementmodel.Element;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.r5.model.ConceptMap;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
|
||||
import org.hl7.fhir.r5.model.Enumerations.BindingStrength;
|
||||
import org.hl7.fhir.r5.model.Property;
|
||||
import org.hl7.fhir.r5.model.Resource;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||
import org.hl7.fhir.r5.model.StructureMap;
|
||||
import org.hl7.fhir.r5.model.StructureMap.StructureMapGroupComponent;
|
||||
|
@ -20,6 +25,12 @@ import org.hl7.fhir.r5.model.StructureMap.StructureMapInputMode;
|
|||
import org.hl7.fhir.r5.model.StructureMap.StructureMapModelMode;
|
||||
import org.hl7.fhir.r5.model.StructureMap.StructureMapStructureComponent;
|
||||
import org.hl7.fhir.r5.model.TypeDetails;
|
||||
import org.hl7.fhir.r5.model.ValueSet;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
|
||||
import org.hl7.fhir.r5.terminologies.ConceptMapUtilities;
|
||||
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
|
||||
import org.hl7.fhir.r5.terminologies.ValueSetUtilities;
|
||||
import org.hl7.fhir.r5.utils.FHIRPathEngine;
|
||||
import org.hl7.fhir.r5.utils.XVerExtensionManager;
|
||||
import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities;
|
||||
|
@ -668,6 +679,46 @@ public class StructureMapValidator extends BaseValidator {
|
|||
ok = false;
|
||||
}
|
||||
break;
|
||||
case "translate":
|
||||
ok = rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() == 3, I18nConstants.SM_TARGET_TRANSFORM_MISSING_PARAMS, transform) && ok;
|
||||
Element srcE = params.size() > 0 ? params.get(0).getNamedChild("value") : null;
|
||||
Element mapE = params.size() > 1? params.get(1).getNamedChild("value") : null;
|
||||
Element modeE = params.size() > 2 ? params.get(2).getNamedChild("value") : null;
|
||||
VariableDefn sv = null;
|
||||
// srcE - if it's an id, the variable must exist
|
||||
if (rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), srcE != null, I18nConstants.SM_TARGET_TRANSFORM_TRANSLATE_NO_PARAM, "source")) {
|
||||
if ("id".equals(srcE.fhirType())) {
|
||||
sv = variables.getVariable(srcE.getValue(), true);
|
||||
rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), sv != null, I18nConstants.SM_TARGET_TRANSFORM_TRANSLATE_UNKNOWN_SOURCE, srcE.getValue());
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
// mapE - it must resolve (may be reference to contained)
|
||||
if (rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), mapE != null, I18nConstants.SM_TARGET_TRANSFORM_TRANSLATE_NO_PARAM, "map_uri")) {
|
||||
String ref = mapE.getValue();
|
||||
ConceptMap cm = null;
|
||||
if (ref.startsWith("#")) {
|
||||
cm = (ConceptMap) loadContainedResource(errors, stack.getLiteralPath(), src, ref.substring(1), ConceptMap.class);
|
||||
ok = rule(errors, "2023-03-01", IssueType.NOTFOUND, target.line(), target.col(), stack.getLiteralPath(), srcE != null, I18nConstants.SM_TARGET_TRANSFORM_TRANSLATE_CM_NOT_FOUND, ref) && ok;
|
||||
} else {
|
||||
// todo: look in Bundle?
|
||||
cm = this.context.fetchResource(ConceptMap.class, ref);
|
||||
warning(errors, "2023-03-01", IssueType.NOTFOUND, target.line(), target.col(), stack.getLiteralPath(), srcE != null, I18nConstants.SM_TARGET_TRANSFORM_TRANSLATE_CM_NOT_FOUND, ref);
|
||||
}
|
||||
if (cm != null && (v != null && v.hasTypeInfo() || (sv != null && sv.hasTypeInfo()))) {
|
||||
ok = checkConceptMap(errors, target.line(), target.col(), stack.getLiteralPath(), cm, sv == null ? null : sv.getEd(), el == null ? null : el.getEd()) && ok;
|
||||
}
|
||||
}
|
||||
if (modeE != null) {
|
||||
String t = modeE.getValue();
|
||||
if (rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), Utilities.existsInList(t, "code", "system", "display", "Coding", "CodeableConcept"), I18nConstants.SM_TARGET_TRANSFORM_TRANSLATE_CM_BAD_MODE, t)) {
|
||||
// cross check the type
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), false, I18nConstants.SM_TARGET_TRANSFORM_NOT_CHECKED, transform);
|
||||
ok = false;
|
||||
|
@ -688,6 +739,58 @@ public class StructureMapValidator extends BaseValidator {
|
|||
}
|
||||
}
|
||||
return ok;
|
||||
|
||||
}
|
||||
|
||||
private boolean checkConceptMap(List<ValidationMessage> errors, int line, int col, String literalPath, ConceptMap cm, ElementDefinition srcED, ElementDefinition tgtED) {
|
||||
boolean ok = true;
|
||||
ValueSet srcVS = null;
|
||||
if (srcED != null) {
|
||||
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, srcED.getBinding().hasValueSet() && srcED.getBinding().getStrength() == BindingStrength.REQUIRED, I18nConstants.SM_TARGET_TRANSLATE_BINDING_SOURCE)) {
|
||||
srcVS = context.fetchResource(ValueSet.class, srcED.getBinding().getValueSet());
|
||||
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, srcVS != null, I18nConstants.SM_TARGET_TRANSLATE_BINDING_VS_SOURCE)) {
|
||||
ValueSetExpansionOutcome vse = context.expandVS(srcVS, true, false);
|
||||
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, vse.isOk(), I18nConstants.SM_TARGET_TRANSLATE_BINDING_VSE_SOURCE, vse.getError())) {
|
||||
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
|
||||
for (ValueSetExpansionContainsComponent c : vse.getValueset().getExpansion().getContains()) {
|
||||
if (ConceptMapUtilities.hasMappingForSource(cm, c.getSystem(), c.getVersion(), c.getCode())) {
|
||||
b.append(c.getCode());
|
||||
}
|
||||
}
|
||||
if (b.count() > 0) {
|
||||
warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, srcED.getBinding().hasValueSet() && srcED.getBinding().getStrength() == BindingStrength.REQUIRED, I18nConstants.SM_TARGET_TRANSLATE_BINDING_SOURCE_UNMAPPED, b.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (srcED != null) {
|
||||
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, tgtED.getBinding().hasValueSet() && tgtED.getBinding().getStrength() == BindingStrength.REQUIRED, I18nConstants.SM_TARGET_TRANSLATE_BINDING_TARGET)) {
|
||||
ValueSet vs = context.fetchResource(ValueSet.class, tgtED.getBinding().getValueSet());
|
||||
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, vs != null, I18nConstants.SM_TARGET_TRANSLATE_BINDING_VS_TARGET)) {
|
||||
ValueSetExpansionOutcome vse = context.expandVS(vs, true, false);
|
||||
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, vse.isOk(), I18nConstants.SM_TARGET_TRANSLATE_BINDING_VSE_TARGET, vse.getError())) {
|
||||
List<String> systems = new ArrayList<>();
|
||||
if (srcVS != null) {
|
||||
for (ConceptSetComponent inc : srcVS.getCompose().getInclude()) {
|
||||
systems.add(inc.getSystem());
|
||||
}
|
||||
}
|
||||
List<Coding> codes = ConceptMapUtilities.listTargets(cm, systems);
|
||||
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
|
||||
for (Coding code : codes) {
|
||||
if (ValueSetUtilities.hasCodeInExpansion(vse.getValueset(), code)) {
|
||||
b.append(code.getCode());
|
||||
}
|
||||
}
|
||||
if (b.count() > 0) {
|
||||
warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, srcED.getBinding().hasValueSet() && srcED.getBinding().getStrength() == BindingStrength.REQUIRED, I18nConstants.SM_TARGET_TRANSLATE_BINDING_TARGET_WRONG, b.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private String inferType(RuleInformation ruleInfo, VariableSet variables, Element rule, String transform, List<Element> params) {
|
||||
|
|
|
@ -126,5 +126,15 @@ public class FHIRPathExpressionFixer {
|
|||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
public static String fixRegex(String regex) {
|
||||
if (regex == null) {
|
||||
return null;
|
||||
}
|
||||
if (regex.equals("-?(0|[1-9][0-9]{0,17})(\\.[0-9]{1,17})?([eE][+-]?[0-9]{1,9}})?")) {
|
||||
return "-?(0|[1-9][0-9]{0,17})(\\.[0-9]{1,17})?([eE](0|[+\\-]?[1-9][0-9]{0,9}))?";
|
||||
}
|
||||
return regex;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue