Merge pull request #1576 from hapifhir/2024-03-gg-codesystem-validation
2024 03 gg codesystem validation
This commit is contained in:
commit
85a31df28e
|
@ -833,19 +833,12 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
|
|||
if ("DomainResource.contained".equals(child.getBase().getPath())) {
|
||||
if (round2) {
|
||||
for (BaseWrapper v : p.getValues()) {
|
||||
if (v.getBase() != null && !RendererFactory.hasSpecificRenderer(v.fhirType())) {
|
||||
if (v.getResource() != null && !RendererFactory.hasSpecificRenderer(v.fhirType())) {
|
||||
x.hr();
|
||||
RenderingContext ctxt = context.copy();
|
||||
ctxt.setContained(true);
|
||||
ResourceRenderer rnd = RendererFactory.factory(v.fhirType(), ctxt);
|
||||
ResourceWrapper rw = null;
|
||||
if (v.getBase() instanceof org.hl7.fhir.r5.elementmodel.Element) {
|
||||
rw = new ElementWrappers.ResourceWrapperMetaElement(ctxt, (org.hl7.fhir.r5.elementmodel.Element) v.getBase());
|
||||
} else if (v.getBase() instanceof Resource){
|
||||
rw = new DirectWrappers.ResourceWrapperDirect(ctxt, (Resource) v.getBase());
|
||||
} else {
|
||||
throw new FHIRException("Not handled: base = "+v.getBase().getClass().getName());
|
||||
}
|
||||
ResourceWrapper rw = v.getResource();
|
||||
rnd.render(x.blockquote(), rw);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ public class BaseWrappers {
|
|||
|
||||
public interface BaseWrapper extends WrapperBase {
|
||||
public Base getBase() throws UnsupportedEncodingException, IOException, FHIRException;
|
||||
public ResourceWrapper getResource() throws UnsupportedEncodingException, IOException, FHIRException; // for contained, etc
|
||||
public PropertyWrapper getChildByName(String tail);
|
||||
public String fhirType();
|
||||
}
|
||||
|
|
|
@ -102,6 +102,19 @@ public class DOMWrappers {
|
|||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceWrapper getResource() throws UnsupportedEncodingException, IOException, FHIRException {
|
||||
Element r = XMLUtil.getFirstChild(element);
|
||||
StructureDefinition sd = getContext().getContext().fetchTypeDefinition(r.getLocalName());
|
||||
if (sd == null) {
|
||||
throw new FHIRException("Unable to find definition for type "+type+" @ "+definition.getPath());
|
||||
}
|
||||
if (sd.getKind() != StructureDefinitionKind.RESOURCE) {
|
||||
throw new FHIRException("Definition for type "+type+" is not for a resource @ "+definition.getPath());
|
||||
}
|
||||
return new ResourceWrapperElement(context, r, sd);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class PropertyWrapperElement extends RendererWrapperImpl implements PropertyWrapper {
|
||||
|
|
|
@ -5,6 +5,7 @@ import java.io.UnsupportedEncodingException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.r5.model.Base;
|
||||
import org.hl7.fhir.r5.model.CanonicalResource;
|
||||
import org.hl7.fhir.r5.model.DomainResource;
|
||||
|
@ -164,6 +165,11 @@ public class DirectWrappers {
|
|||
return wrapped.fhirType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceWrapper getResource() throws UnsupportedEncodingException, IOException, FHIRException {
|
||||
return new DirectWrappers.ResourceWrapperDirect(getContext(), (Resource) wrapped);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ResourceWrapperDirect extends WrapperBaseImpl implements ResourceWrapper {
|
||||
|
|
|
@ -109,6 +109,11 @@ public class ElementWrappers {
|
|||
return element.fhirType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceWrapper getResource() throws UnsupportedEncodingException, IOException, FHIRException {
|
||||
return new ElementWrappers.ResourceWrapperMetaElement(getContext(), element);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ResourceWrapperMetaElement extends WrapperBaseImpl implements ResourceWrapper {
|
||||
|
|
|
@ -234,6 +234,15 @@ public class CodeSystemUtilities extends TerminologyUtilities {
|
|||
concept.addProperty().setCode(code).setValue(value);
|
||||
}
|
||||
|
||||
public static void setProperty(CodeSystem cs, ConceptDefinitionComponent concept, String url, String code, DataType value) throws FHIRFormatError {
|
||||
defineProperty(cs, code, propertyTypeForValue(value), url);
|
||||
ConceptPropertyComponent p = getProperty(concept, code);
|
||||
if (p != null)
|
||||
p.setValue(value);
|
||||
else
|
||||
concept.addProperty().setCode(code).setValue(value);
|
||||
}
|
||||
|
||||
|
||||
private static PropertyType propertyTypeForValue(DataType value) {
|
||||
if (value instanceof BooleanType) {
|
||||
|
@ -262,6 +271,9 @@ public class CodeSystemUtilities extends TerminologyUtilities {
|
|||
|
||||
private static String defineProperty(CodeSystem cs, String code, PropertyType pt) {
|
||||
String url = "http://hl7.org/fhir/concept-properties#"+code;
|
||||
return defineProperty(cs, code, pt, url);
|
||||
}
|
||||
private static String defineProperty(CodeSystem cs, String code, PropertyType pt, String url) {
|
||||
for (PropertyComponent p : cs.getProperty()) {
|
||||
if (p.hasCode() && p.getCode().equals(code)) {
|
||||
if (!p.getUri().equals(url)) {
|
||||
|
|
|
@ -2235,4 +2235,11 @@ public class Utilities {
|
|||
return true;
|
||||
}
|
||||
|
||||
public static String stripEoln(String text) {
|
||||
if (text == null) {
|
||||
return "";
|
||||
}
|
||||
return text.replace("\r\n", " ").replace("\n", " ").replace("\r", " ");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1101,6 +1101,7 @@ public class I18nConstants {
|
|||
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";
|
||||
public static final String CODESYSTEM_PROPERTY_CODE_WARNING = "CODESYSTEM_PROPERTY_CODE_WARNING";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1159,4 +1159,5 @@ VALUESET_BAD_FILTER_OP = The operation ''{0}'' is not allowed for property ''{1}
|
|||
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)
|
||||
CODESYSTEM_PROPERTY_CODE_WARNING = If the type is ''code'', then the valueSet property should be provided to clarify what kind of code will be found in the element
|
||||
|
||||
|
|
|
@ -3344,7 +3344,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
Set<String> refs = new HashSet<>();
|
||||
int count = countTargetMatches(resource, ref, true, "$", refs);
|
||||
if (count == 0) {
|
||||
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_XHTML_RESOLVE, href, xpath, node.allText());
|
||||
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_XHTML_RESOLVE, href, xpath, Utilities.stripEoln(node.allText()));
|
||||
} else if (count > 1) {
|
||||
warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_XHTML_MULTIPLE_MATCHES, href, xpath, node.allText(), CommaSeparatedStringBuilder.join(", ", refs));
|
||||
}
|
||||
|
@ -5760,7 +5760,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
}
|
||||
|
||||
if (rule(errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), wg != null && !url.contains("http://hl7.org/fhir/sid"), I18nConstants.VALIDATION_HL7_WG_NEEDED, ToolingExtensions.EXT_WORKGROUP)) {
|
||||
if (rule(errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), wg != null || url.contains("http://hl7.org/fhir/sid"), I18nConstants.VALIDATION_HL7_WG_NEEDED, ToolingExtensions.EXT_WORKGROUP)) {
|
||||
HL7WorkGroup wgd = HL7WorkGroups.find(wg);
|
||||
if (rule(errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), wgd != null, I18nConstants.VALIDATION_HL7_WG_UNKNOWN, wg)) {
|
||||
String rpub = "HL7 International / "+wgd.getName();
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
|
|||
import org.hl7.fhir.r5.model.ValueSet;
|
||||
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
|
||||
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;
|
||||
|
@ -57,16 +58,30 @@ public class CodeSystemValidator extends BaseValidator {
|
|||
|
||||
}
|
||||
|
||||
public enum CodeValidationRule {
|
||||
NO_VALIDATION, INTERNAL_CODE, VS_ERROR, VS_WARNING
|
||||
}
|
||||
|
||||
public class PropertyDef {
|
||||
private String uri;
|
||||
private String code;
|
||||
private String type;
|
||||
|
||||
private CodeValidationRule rule;
|
||||
private String valueset;
|
||||
|
||||
protected PropertyDef(String uri, String code, String type) {
|
||||
super();
|
||||
this.uri = uri;
|
||||
this.code = code;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public void setCodeValidationRules(CodeValidationRule rule, String valueset) {
|
||||
this.rule = rule;
|
||||
this.valueset = valueset;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
@ -76,9 +91,14 @@ public class CodeSystemValidator extends BaseValidator {
|
|||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public String getValueset() {
|
||||
return valueset;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final String VS_PROP_STATUS = null;
|
||||
|
||||
public CodeSystemValidator(BaseValidator parent) {
|
||||
super(parent);
|
||||
}
|
||||
|
@ -252,8 +272,7 @@ public class CodeSystemValidator extends BaseValidator {
|
|||
ukp = KnownProperty.ItemWeight;
|
||||
break;
|
||||
default:
|
||||
ok = false;
|
||||
rule(errors, "2024-03-06", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), false, I18nConstants.CODESYSTEM_PROPERTY_BAD_HL7_URI, uri);
|
||||
ok = rule(errors, "2024-03-06", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), isBaseSpec(cs.getNamedChildValue("url")), I18nConstants.CODESYSTEM_PROPERTY_BAD_HL7_URI, uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -309,7 +328,31 @@ public class CodeSystemValidator extends BaseValidator {
|
|||
if (type != null) {
|
||||
ok = rule(errors, "2024-03-06", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), type.equals(ukp.getType()), I18nConstants.CODESYSTEM_PROPERTY_URI_TYPE_MISMATCH, uri, ukp.getType(),type) && ok;
|
||||
}
|
||||
switch (ukp) {
|
||||
case Child:
|
||||
case Parent:
|
||||
case PartOf:
|
||||
case Synonym:
|
||||
pd.setCodeValidationRules(CodeValidationRule.INTERNAL_CODE, null);
|
||||
break;
|
||||
case Status:
|
||||
pd.setCodeValidationRules(CodeValidationRule.VS_WARNING, VS_PROP_STATUS);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if ("code".equals(pd.getType())) {
|
||||
if (property.hasExtension("http://hl7.org/fhir/6.0/StructureDefinition/extension-CodeSystem.property.valueSet")) {
|
||||
pd.setCodeValidationRules(CodeValidationRule.VS_ERROR, property.getExtensionValue("http://hl7.org/fhir/6.0/StructureDefinition/extension-CodeSystem.property.valueSet").primitiveValue());
|
||||
} else if (VersionUtilities.isR6Plus(context.getVersion())) {
|
||||
hint(errors, "2024-03-18", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), ukp != null && type.equals(ukp.getType()), I18nConstants.CODESYSTEM_PROPERTY_CODE_WARNING);
|
||||
} else {
|
||||
|
||||
}
|
||||
} else if ("Coding".equals(pd.getType()) && property.hasExtension("http://hl7.org/fhir/6.0/StructureDefinition/extension-CodeSystem.property.valueSet")) {
|
||||
pd.setCodeValidationRules(CodeValidationRule.VS_ERROR, property.getExtensionValue("http://hl7.org/fhir/6.0/StructureDefinition/extension-CodeSystem.property.valueSet").primitiveValue());
|
||||
}
|
||||
|
||||
if (uri == null) {
|
||||
if (ckp == null) {
|
||||
hint(errors, "2024-03-06", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), false, I18nConstants.CODESYSTEM_PROPERTY_UNKNOWN_CODE, code);
|
||||
|
@ -323,6 +366,10 @@ public class CodeSystemValidator extends BaseValidator {
|
|||
return ok;
|
||||
}
|
||||
|
||||
private boolean isBaseSpec(String url) {
|
||||
return url.startsWith("http://hl7.org/fhir/") && !url.substring(20).contains("/");
|
||||
}
|
||||
|
||||
private boolean checkConcept(List<ValidationMessage> errors, Element cs, NodeStack stack, boolean caseSensitive, String hierarchyMeaning, CodeSystem csB, Element concept, Set<String> codes, Map<String, PropertyDef> properties) {
|
||||
boolean ok = true;
|
||||
String code = concept.getNamedChildValue("code");
|
||||
|
|
Loading…
Reference in New Issue