Merge branch 'master' into 1509-address-line-conversion-fix
This commit is contained in:
commit
2cb3375cda
|
@ -21,3 +21,5 @@
|
|||
(Unknown license) jakarta-regexp (jakarta-regexp:jakarta-regexp:1.4 - no url defined)
|
||||
# License string includes nested brackets, causing parser breakage, but is a valid BSD license.
|
||||
(BSD 3-Clause "New" or "Revised" License (BSD-3-Clause)) abego TreeLayout Core (org.abego.treelayout:org.abego.treelayout.core:1.0.3 - http://treelayout.sourceforge.net)
|
||||
# License name includes brackets (javax.xml.bind)
|
||||
(The Apache Software License, Version 2.0) Jackson module: Old JAXB Annotations (javax.xml.bind) (com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.16.0 - https://github.com/FasterXML/jackson-modules-base)
|
|
@ -3,6 +3,11 @@
|
|||
* Fix bug where validator doesn't actually validate web sourced input
|
||||
* Fix narrative link validation and add id/idref validation
|
||||
* Remove fhir-test-cases from Validator CLI JAR (#1497) (reduce size)
|
||||
* Fix to CDA xsi:type validation per SD decision
|
||||
* Apply regex pattern to literal format if defined
|
||||
* Improvements to vital signs related messages
|
||||
* Fix R4 con-3 FHIRPath expression
|
||||
* Fix bug loading packages with partially specified version that doesn't exist
|
||||
|
||||
## Other code changes
|
||||
|
||||
|
@ -20,4 +25,5 @@
|
|||
* Give kinder error message for missing param
|
||||
* Fix commonmark group and bump version (#1500)
|
||||
* Remove dep used for local testing
|
||||
* Bump jackson & logback versions
|
||||
* Fix StringType element properties not being copied in various Address, HumanName convertors
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
package org.hl7.fhir.r4.ips;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hl7.fhir.r4.ips.IPSRenderer.InternalTemplateEngine;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Composition;
|
||||
import org.hl7.fhir.r4.model.Composition.SectionComponent;
|
||||
import org.hl7.fhir.r4.model.DomainResource;
|
||||
import org.hl7.fhir.r4.model.Reference;
|
||||
import org.hl7.fhir.r4.terminologies.TerminologyClient;
|
||||
import org.hl7.fhir.utilities.xhtml.NodeType;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||
|
||||
public class IPSRenderer {
|
||||
|
||||
public class InternalTemplateEngine implements ITemplateImplementer {
|
||||
|
||||
@Override
|
||||
public String buildPage(Map<String, String> headers, String content) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private interface ITemplateImplementer {
|
||||
public String buildPage(Map<String, String> headers, String content);
|
||||
}
|
||||
private TerminologyClient tx;
|
||||
private String folder; // for images etc
|
||||
private Map<String, byte[]> binaries; // from the pubpack
|
||||
private ITemplateImplementer templater;
|
||||
private Map<String, String> headers;
|
||||
|
||||
public IPSRenderer(TerminologyClient tx, String folder, Map<String, byte[]> binaries, ITemplateImplementer templater) {
|
||||
super();
|
||||
this.tx = tx;
|
||||
this.folder = folder;
|
||||
this.binaries = binaries;
|
||||
this.templater = templater;
|
||||
}
|
||||
|
||||
public IPSRenderer(TerminologyClient tx, String folder, Map<String, byte[]> binaries) {
|
||||
super();
|
||||
this.tx = tx;
|
||||
this.folder = folder;
|
||||
this.binaries = binaries;
|
||||
this.templater = new InternalTemplateEngine();
|
||||
}
|
||||
|
||||
public String render(Bundle document) throws IOException {
|
||||
headers = new HashMap<>();
|
||||
XhtmlNode x = new XhtmlNode(NodeType.Element, "div");
|
||||
generate(x, document);
|
||||
return templater.buildPage(headers, new XhtmlComposer(false, true).compose(x));
|
||||
}
|
||||
|
||||
private void generate(XhtmlNode x, Bundle document) {
|
||||
Composition cmp = (Composition) document.getEntryFirstRep().getResource();
|
||||
int sectionDepth = findSectionDepth(cmp.getSection());
|
||||
XhtmlNode table = x.table("grid");
|
||||
|
||||
// row 1: header
|
||||
XhtmlNode tr = table.tr();
|
||||
XhtmlNode td = tr.td().colspan(1+sectionDepth);
|
||||
td.b().tx("Provided");
|
||||
td = tr.td().colspan(1+sectionDepth);
|
||||
td.b().tx("Generated");
|
||||
|
||||
// row 2: Subject
|
||||
DomainResource subject = findResource(document, cmp.getSubject());
|
||||
tr = table.tr();
|
||||
td = tr.td().colspan(1+sectionDepth);
|
||||
// genNarrative("subject", subject, td);
|
||||
td = tr.td().colspan(1+sectionDepth);
|
||||
td.b().tx("Generated");
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private DomainResource findResource(Bundle document, Reference subject) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
private int findSectionDepth(List<SectionComponent> list) {
|
||||
int i = 1;
|
||||
for (SectionComponent sect : list) {
|
||||
i = Integer.max(i, 1+findSectionDepth(sect.getSection()));
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
|
@ -41,6 +41,7 @@ import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.SourcedChildDefiniti
|
|||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.fhirpath.TypeDetails;
|
||||
import org.hl7.fhir.r5.formats.FormatUtilities;
|
||||
import org.hl7.fhir.r5.model.Constants;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition.PropertyRepresentation;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
|
||||
|
@ -345,12 +346,13 @@ public class Property {
|
|||
}
|
||||
ElementDefinition ed = definition;
|
||||
StructureDefinition sd = structure;
|
||||
boolean isCDA = isCDAElement(structure);
|
||||
SourcedChildDefinitions children = profileUtilities.getChildMap(sd, ed);
|
||||
String url = null;
|
||||
if (children.getList().isEmpty() || isElementWithOnlyExtension(ed, children.getList())) {
|
||||
// ok, find the right definitions
|
||||
String t = null;
|
||||
if (ed.getType().size() == 1)
|
||||
if (ed.getType().size() == 1 && (statedType == null || !isCDA))
|
||||
t = ed.getType().get(0).getWorkingCode();
|
||||
else if (ed.getType().size() == 0)
|
||||
throw new Error("types == 0, and no children found on "+getDefinition().getPath());
|
||||
|
@ -363,9 +365,9 @@ public class Property {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (!all) {
|
||||
if (!all || (isCDA && statedType != null)) {
|
||||
// ok, it's polymorphic
|
||||
if (ed.hasRepresentation(PropertyRepresentation.TYPEATTR)) {
|
||||
if (ed.hasRepresentation(PropertyRepresentation.TYPEATTR) || isCDA) {
|
||||
t = statedType;
|
||||
if (t == null && ToolingExtensions.hasExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype"))
|
||||
t = ToolingExtensions.readStringExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype");
|
||||
|
@ -379,13 +381,21 @@ public class Property {
|
|||
url = tr.getWorkingCode();
|
||||
ok = true;
|
||||
}
|
||||
if (!ok) {
|
||||
sdt = findAncestor(t, sdt);
|
||||
if (sdt != null) {
|
||||
url = sdt.getUrl();
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ok)
|
||||
if (ok) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
throw new DefinitionException("Type '"+t+"' is not an acceptable type for '"+elementName+"' on property "+definition.getPath());
|
||||
}
|
||||
if (!ok)
|
||||
throw new DefinitionException("Type '"+t+"' is not an acceptable type for '"+elementName+"' on property "+definition.getPath());
|
||||
|
||||
} else {
|
||||
t = elementName.substring(tail(ed.getPath()).length() - 3);
|
||||
if (isPrimitive(lowFirst(t)))
|
||||
|
@ -421,6 +431,26 @@ public class Property {
|
|||
return properties;
|
||||
}
|
||||
|
||||
private StructureDefinition findAncestor(String type, StructureDefinition sdt) {
|
||||
if (sdt != null) {
|
||||
StructureDefinition sd = context.fetchTypeDefinition(type);
|
||||
StructureDefinition t = sd;
|
||||
while (t != null) {
|
||||
if (t == sdt) {
|
||||
return sd;
|
||||
}
|
||||
t = context.fetchResource(StructureDefinition.class, t.getBaseDefinition());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private boolean isCDAElement(StructureDefinition sd) {
|
||||
return sd.hasUrl() && sd.getUrl().startsWith(Constants.NS_CDA_ROOT);
|
||||
}
|
||||
|
||||
|
||||
protected List<Property> getChildProperties(TypeDetails type) throws DefinitionException {
|
||||
ElementDefinition ed = definition;
|
||||
StructureDefinition sd = structure;
|
||||
|
|
|
@ -60,6 +60,7 @@ import org.hl7.fhir.r5.elementmodel.Element.SpecialElement;
|
|||
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
||||
import org.hl7.fhir.r5.formats.FormatUtilities;
|
||||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||
import org.hl7.fhir.r5.model.Constants;
|
||||
import org.hl7.fhir.r5.model.DateTimeType;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition.PropertyRepresentation;
|
||||
|
@ -232,7 +233,7 @@ public class XmlParser extends ParserBase {
|
|||
|
||||
Element result = new Element(element.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd)).setFormat(FhirFormat.XML);
|
||||
result.setPath(element.getLocalName());
|
||||
checkElement(errors, element, path, result.getProperty(), false);
|
||||
checkElement(errors, element, result, path, result.getProperty(), false);
|
||||
result.markLocation(line(element, false), col(element, false));
|
||||
result.setType(element.getLocalName());
|
||||
parseChildren(errors, path, element, result);
|
||||
|
@ -274,7 +275,7 @@ public class XmlParser extends ParserBase {
|
|||
return true;
|
||||
}
|
||||
|
||||
private void checkElement(List<ValidationMessage> errors, org.w3c.dom.Element element, String path, Property prop, boolean xsiTypeChecked) throws FHIRFormatError {
|
||||
private void checkElement(List<ValidationMessage> errors, org.w3c.dom.Element element, Element e, String path, Property prop, boolean xsiTypeChecked) throws FHIRFormatError {
|
||||
if (policy == ValidationPolicy.EVERYTHING) {
|
||||
if (empty(element) && FormatUtilities.FHIR_NS.equals(element.getNamespaceURI())) // this rule only applies to FHIR Content
|
||||
logError(errors, ValidationMessage.NO_RULE_DATE, line(element, false), col(element, false), path, IssueType.INVALID, context.formatMessage(I18nConstants.ELEMENT_MUST_HAVE_SOME_CONTENT), IssueSeverity.ERROR);
|
||||
|
@ -290,22 +291,40 @@ public class XmlParser extends ParserBase {
|
|||
String xsiType = element.getAttributeNS(FormatUtilities.NS_XSI, "type");
|
||||
if (!Utilities.noString(xsiType)) {
|
||||
String actualType = prop.getXmlTypeName();
|
||||
if (!xsiType.equals(actualType)) {
|
||||
logError(errors, "2023-10-12", line(element, false), col(element, false), path, IssueType.INVALID, context.formatMessage(I18nConstants.XSI_TYPE_WRONG, xsiType, actualType), IssueSeverity.ERROR);
|
||||
} else {
|
||||
if (xsiType.equals(actualType)) {
|
||||
logError(errors, "2023-10-12", line(element, false), col(element, false), path, IssueType.INVALID, context.formatMessage(I18nConstants.XSI_TYPE_UNNECESSARY), IssueSeverity.INFORMATION);
|
||||
} else {
|
||||
StructureDefinition sd = findLegalConstraint(xsiType, actualType);
|
||||
if (sd != null) {
|
||||
e.setType(sd.getType());
|
||||
e.setExplicitType(xsiType);
|
||||
} else {
|
||||
logError(errors, "2023-10-12", line(element, false), col(element, false), path, IssueType.INVALID, context.formatMessage(I18nConstants.XSI_TYPE_WRONG, xsiType, actualType), IssueSeverity.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private StructureDefinition findLegalConstraint(String xsiType, String actualType) {
|
||||
StructureDefinition sdA = context.fetchTypeDefinition(actualType);
|
||||
StructureDefinition sd = context.fetchTypeDefinition(xsiType);
|
||||
while (sd != null) {
|
||||
if (sd == sdA) {
|
||||
return sd;
|
||||
}
|
||||
sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Element parse(List<ValidationMessage> errors, org.w3c.dom.Element base, String type) throws Exception {
|
||||
StructureDefinition sd = getDefinition(errors, 0, 0, FormatUtilities.FHIR_NS, type);
|
||||
Element result = new Element(base.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd)).setFormat(FhirFormat.XML).setNativeObject(base);
|
||||
result.setPath(base.getLocalName());
|
||||
String path = "/"+pathPrefix(base.getNamespaceURI())+base.getLocalName();
|
||||
checkElement(errors, base, path, result.getProperty(), false);
|
||||
checkElement(errors, base, result, path, result.getProperty(), false);
|
||||
result.setType(base.getLocalName());
|
||||
parseChildren(errors, path, base, result);
|
||||
result.numberChildren();
|
||||
|
@ -469,7 +488,7 @@ public class XmlParser extends ParserBase {
|
|||
} else
|
||||
n.setType(n.getType());
|
||||
}
|
||||
checkElement(errors, (org.w3c.dom.Element) child, npath, n.getProperty(), xsiTypeChecked);
|
||||
checkElement(errors, (org.w3c.dom.Element) child, n, npath, n.getProperty(), xsiTypeChecked);
|
||||
element.getChildren().add(n);
|
||||
if (ok) {
|
||||
if (property.isResource())
|
||||
|
@ -500,7 +519,7 @@ public class XmlParser extends ParserBase {
|
|||
Element n = new Element(name, property).markLocation(line(child, false), col(child, false)).setFormat(FhirFormat.XML).setNativeObject(child);
|
||||
cgn.getChildren().add(n);
|
||||
n.setPath(element.getPath()+"."+property.getName());
|
||||
checkElement(errors, (org.w3c.dom.Element) child, npath, n.getProperty(), false);
|
||||
checkElement(errors, (org.w3c.dom.Element) child, n, npath, n.getProperty(), false);
|
||||
parseChildren(errors, npath, (org.w3c.dom.Element) child, n);
|
||||
}
|
||||
}
|
||||
|
@ -749,6 +768,11 @@ public class XmlParser extends ParserBase {
|
|||
if (hasTypeAttr(c))
|
||||
return true;
|
||||
}
|
||||
// xsi_type is always allowed on CDA elements. right now, I'm not sure where to indicate this in the model,
|
||||
// so it's just hardcoded here
|
||||
if (e.getType() != null && e.getType().startsWith(Constants.NS_CDA_ROOT)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -701,8 +701,9 @@ public class RenderingContext {
|
|||
public GenerationRules getRules() {
|
||||
return rules;
|
||||
}
|
||||
public void setRules(GenerationRules rules) {
|
||||
public RenderingContext setRules(GenerationRules rules) {
|
||||
this.rules = rules;
|
||||
return this;
|
||||
}
|
||||
public StandardsStatus getDefaultStandardsStatus() {
|
||||
return defaultStandardsStatus;
|
||||
|
|
|
@ -63,6 +63,12 @@ public class CanonicalResourceUtilities {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* for use in the core build where the context is not fully populated. Only known safe for R6 resources
|
||||
*
|
||||
* @param res
|
||||
* @param code
|
||||
*/
|
||||
public static void setHl7WG(org.w3c.dom.Element res, String code) {
|
||||
String rt = res.getNodeName();
|
||||
if (VersionUtilities.getExtendedCanonicalResourceNames("5.0.0").contains(rt)) {
|
||||
|
@ -82,14 +88,12 @@ public class CanonicalResourceUtilities {
|
|||
if (wgext == null) {
|
||||
wgext = res.getOwnerDocument().createElementNS(Constants.NS_FHIR_ROOT, "extension");
|
||||
wgext.setAttribute("url", ToolingExtensions.EXT_WORKGROUP);
|
||||
org.w3c.dom.Element after = XMLUtil.getFirstChild(res, "modifierExtension", "url", "identifier", "version", "status");
|
||||
if (after == null) {
|
||||
after = XMLUtil.getLastChild(res, "id", "meta", "text", "implicitRules", "language", "text", "contained");
|
||||
if (after != null) {
|
||||
after = XMLUtil.getNextSibling(after);
|
||||
}
|
||||
org.w3c.dom.Element after = XMLUtil.getLastChild(res, "id", "meta", "text", "implicitRules", "language", "text", "contained");
|
||||
if (after != null) {
|
||||
after = XMLUtil.getNextSibling(after);
|
||||
}
|
||||
res.insertBefore(wgext, after);
|
||||
res.insertBefore(res.getOwnerDocument().createTextNode("\n "), after);
|
||||
}
|
||||
XMLUtil.clearChildren(wgext);
|
||||
org.w3c.dom.Element valueCode = res.getOwnerDocument().createElementNS(Constants.NS_FHIR_ROOT, "valueCode");
|
||||
|
@ -99,10 +103,55 @@ public class CanonicalResourceUtilities {
|
|||
org.w3c.dom.Element pub = XMLUtil.getNamedChild(res, "publisher");
|
||||
if (pub == null) {
|
||||
pub = res.getOwnerDocument().createElementNS(Constants.NS_FHIR_ROOT, "publisher");
|
||||
org.w3c.dom.Element after = XMLUtil.getFirstChild(res, "contact", "description", "useContext", "jurisdiction", "purpose", "copyright");
|
||||
org.w3c.dom.Element after = XMLUtil.getLastChild(res, "id", "meta", "text", "implicitRules", "language", "text", "contained", "extension", "modifierExtension",
|
||||
"url", "identifier", "version", "versionAlgorithmString", "versionAlgorithmCoding", "name", "title", "status", "experimental", "date", ("EvidenceReport".equals(rt) ? "subject" : "xx"));
|
||||
if (after != null) {
|
||||
after = XMLUtil.getNextSibling(after);
|
||||
}
|
||||
res.insertBefore(pub, after);
|
||||
res.insertBefore(res.getOwnerDocument().createTextNode("\n "), after);
|
||||
}
|
||||
pub.setAttribute("value", "HL7 International / "+wg.getName());
|
||||
|
||||
org.w3c.dom.Element contact = XMLUtil.getNamedChild(res, "contact");
|
||||
if (contact == null) {
|
||||
contact = res.getOwnerDocument().createElementNS(Constants.NS_FHIR_ROOT, "contact");
|
||||
res.insertBefore(contact, XMLUtil.getNextSibling(pub));
|
||||
res.insertBefore(res.getOwnerDocument().createTextNode("\n "), contact.getNextSibling());
|
||||
}
|
||||
|
||||
org.w3c.dom.Element telecom = XMLUtil.getNamedChild(contact, "telecom");
|
||||
if (telecom == null) {
|
||||
contact.appendChild(res.getOwnerDocument().createTextNode("\n "));
|
||||
telecom = res.getOwnerDocument().createElementNS(Constants.NS_FHIR_ROOT, "telecom");
|
||||
contact.appendChild(telecom);
|
||||
contact.appendChild(res.getOwnerDocument().createTextNode("\n "));
|
||||
}
|
||||
|
||||
org.w3c.dom.Element system = XMLUtil.getNamedChild(telecom, "system");
|
||||
if (system == null) {
|
||||
system = res.getOwnerDocument().createElementNS(Constants.NS_FHIR_ROOT, "system");
|
||||
org.w3c.dom.Element after = XMLUtil.getLastChild(telecom, "id", "extension");
|
||||
if (after != null) {
|
||||
after = XMLUtil.getNextSibling(after);
|
||||
}
|
||||
telecom.insertBefore(system, after);
|
||||
telecom.insertBefore(res.getOwnerDocument().createTextNode("\n "), after);
|
||||
}
|
||||
system.setAttribute("value", "url");
|
||||
|
||||
|
||||
org.w3c.dom.Element value = XMLUtil.getNamedChild(telecom, "value");
|
||||
if (value == null) {
|
||||
value = res.getOwnerDocument().createElementNS(Constants.NS_FHIR_ROOT, "value");
|
||||
org.w3c.dom.Element after = XMLUtil.getLastChild(telecom, "id", "extension", "system");
|
||||
if (after != null) {
|
||||
after = XMLUtil.getNextSibling(after);
|
||||
}
|
||||
telecom.insertBefore(system, after);
|
||||
telecom.insertBefore(res.getOwnerDocument().createTextNode("\n "), after);
|
||||
}
|
||||
value.setAttribute("value", wg.getLink());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,15 @@ public class HL7WorkGroups {
|
|||
public static class HL7WorkGroup {
|
||||
private String link;
|
||||
private String name;
|
||||
private String name2;
|
||||
private String code;
|
||||
|
||||
|
||||
protected HL7WorkGroup(String code, String name, String link) {
|
||||
protected HL7WorkGroup(String code, String name, String name2, String link) {
|
||||
super();
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.name2 = name2;
|
||||
this.link = link;
|
||||
}
|
||||
public String getLink() {
|
||||
|
@ -20,6 +22,9 @@ public class HL7WorkGroups {
|
|||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public String getName2() {
|
||||
return name2;
|
||||
}
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
@ -27,9 +32,10 @@ public class HL7WorkGroups {
|
|||
|
||||
public static HL7WorkGroup find(String wg) {
|
||||
String name = nameForWG(wg);
|
||||
String name2 = name2ForWG(wg);
|
||||
String url = urlForWG(wg);
|
||||
if (name != null) {
|
||||
return new HL7WorkGroup(wg, name, url);
|
||||
return new HL7WorkGroup(wg, name, name2, url);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -125,7 +131,7 @@ public class HL7WorkGroups {
|
|||
case "sd": return "Structured Documents";
|
||||
case "sec": return "Security";
|
||||
case "soa": return "Services Oriented Architecture";
|
||||
case "ti": return "Vocabulary";
|
||||
case "ti": return "Terminology Infrastructure";
|
||||
case "tsmg": return "Terminology Services Management Group (TSMG)";
|
||||
case "us": return "US Realm Steering Committee";
|
||||
case "v2": return "V2 Management Group";
|
||||
|
@ -134,5 +140,12 @@ public class HL7WorkGroups {
|
|||
return null;
|
||||
}
|
||||
|
||||
private static String name2ForWG(String wg) {
|
||||
switch (wg) {
|
||||
case "ti": return "Vocabulary";
|
||||
case "vocab": return "Vocabulary";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -534,6 +534,7 @@ public class I18nConstants {
|
|||
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_TYPE_ALT = "TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX_TYPE_ALT";
|
||||
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";
|
||||
|
@ -626,6 +627,7 @@ public class I18nConstants {
|
|||
public static final String VALIDATION_VAL_PROFILE_MATCHMULTIPLE = "Validation_VAL_Profile_MatchMultiple";
|
||||
public static final String VALIDATION_VAL_PROFILE_MAXIMUM = "Validation_VAL_Profile_Maximum";
|
||||
public static final String VALIDATION_VAL_PROFILE_MINIMUM = "Validation_VAL_Profile_Minimum";
|
||||
public static final String VALIDATION_VAL_PROFILE_MINIMUM_MAGIC = "VALIDATION_VAL_PROFILE_MINIMUM_MAGIC";
|
||||
public static final String VALIDATION_VAL_PROFILE_MULTIPLEMATCHES = "Validation_VAL_Profile_MultipleMatches";
|
||||
public static final String VALIDATION_VAL_PROFILE_NOCHECKMAX = "Validation_VAL_Profile_NoCheckMax";
|
||||
public static final String VALIDATION_VAL_PROFILE_NOCHECKMIN = "Validation_VAL_Profile_NoCheckMin";
|
||||
|
@ -997,6 +999,7 @@ public class I18nConstants {
|
|||
public static final String VALIDATION_HL7_WG_NEEDED = "VALIDATION_HL7_WG_NEEDED";
|
||||
public static final String VALIDATION_HL7_WG_UNKNOWN = "VALIDATION_HL7_WG_UNKNOWN";
|
||||
public static final String VALIDATION_HL7_PUBLISHER_MISMATCH = "VALIDATION_HL7_PUBLISHER_MISMATCH";
|
||||
public static final String VALIDATION_HL7_PUBLISHER_MISMATCH2 = "VALIDATION_HL7_PUBLISHER_MISMATCH2";
|
||||
public static final String VALIDATION_HL7_WG_URL = "VALIDATION_HL7_WG_URL";
|
||||
public static final String VALIDATION_HL7_PUBLISHER_MISSING = "VALIDATION_HL7_PUBLISHER_MISSING";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_UCUM_ANNOTATIONS = "TYPE_SPECIFIC_CHECKS_DT_QTY_UCUM_ANNOTATIONS";
|
||||
|
@ -1034,6 +1037,7 @@ public class I18nConstants {
|
|||
public static final String BUNDLE_BUNDLE_ENTRY_FOUND_MULTIPLE_FRAGMENT = "BUNDLE_BUNDLE_ENTRY_FOUND_MULTIPLE_FRAGMENT";
|
||||
public static final String XHTML_IDREF_NOT_FOUND = "XHTML_IDREF_NOT_FOUND";
|
||||
public static final String XHTML_IDREF_NOT_MULTIPLE_MATCHES = "XHTML_IDREF_NOT_MULTIPLE_MATCHES";
|
||||
public static final String SD_CONTEXT_SHOULD_NOT_BE_FHIRPATH = "SD_CONTEXT_SHOULD_NOT_BE_FHIRPATH";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -75,6 +75,9 @@ public abstract class BasePackageCacheManager implements IPackageCacheManager {
|
|||
}
|
||||
if (version.endsWith(".x")) {
|
||||
version = packageClient.getLatestVersion(id, version);
|
||||
if (version == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
InputStream stream = packageClient.fetch(id, version);
|
||||
|
|
|
@ -222,6 +222,7 @@ 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_REGEX_TYPE_ALT = Neither the element value ''{0}'' or the formatted value ''{1}'' meet {2} regex ''{3}''
|
||||
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)
|
||||
|
@ -243,6 +244,7 @@ Validation_VAL_Profile_Maximum_one = {3}: max allowed = {7}, but found {0} (from
|
|||
Validation_VAL_Profile_Maximum_other = {3}: max allowed = {7}, but found {0} (from {1})
|
||||
Validation_VAL_Profile_Minimum_one = {3}: minimum required = {7}, but only found {0} (from {1})
|
||||
Validation_VAL_Profile_Minimum_other = {3}: minimum required = {7}, but only found {0} (from {1})
|
||||
VALIDATION_VAL_PROFILE_MINIMUM_MAGIC = {0}: magic LOINC code {1} required, but not found (from {2}). Note that other Observation codes are allowed in addition to this required magic code
|
||||
Validation_VAL_Profile_NoCheckMax_one = {3}: Found {0} match, but unable to check max allowed ({2}) due to lack of slicing validation (from {1})
|
||||
Validation_VAL_Profile_NoCheckMax_other = {3}: Found {0} matches, but unable to check max allowed ({2}) due to lack of slicing validation (from {1})
|
||||
Validation_VAL_Profile_NoCheckMin_one = {3}: Found {0} match, but unable to check minimum required ({2}) due to lack of slicing validation (from {1})
|
||||
|
@ -933,6 +935,7 @@ NO_VALID_DISPLAY_FOUND_other = No valid Display Names found for {1}#{2} in the l
|
|||
SD_NO_CONTEXT_WHEN_NOT_EXTENSION = The type is {0} so an extension context should not be specified
|
||||
SD_NO_CONTEXT_INV_WHEN_NOT_EXTENSION = The type is {0} so an extension context invariants should not be specified
|
||||
SD_CONTEXT_SHOULD_NOT_BE_ELEMENT = Review the extension type for {1}: extensions should not have a context of {0} unless it''s really intended that they can be used anywhere
|
||||
SD_CONTEXT_SHOULD_NOT_BE_FHIRPATH = Review the extension type for {1}: the context of {0} appears to be a simple element, so the context type should be 'element' not 'fhirpath'
|
||||
ED_PATH_WRONG_TYPE_MATCH = The path must be ''{0}'' not ''{1}'' when the type list is not constrained
|
||||
ATTEMPT_TO_CHANGE_SLICING = The element at {0} defines the slicing {1} but then an element in the slicing {2} tries to redefine the slicing to {3}
|
||||
REPEAT_SLICING_IGNORED = The element at {0} defines the slicing but then an element in the slicing {2} repeats it, which is ignored
|
||||
|
@ -1057,6 +1060,7 @@ BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_NO_MATCH_REASON = The {1} resource did no
|
|||
VALIDATION_HL7_WG_NEEDED = When HL7 is publishing a resource, the owning committee must be stated using the {0} extension
|
||||
VALIDATION_HL7_WG_UNKNOWN = The nominated WG ''{0}'' is unknown
|
||||
VALIDATION_HL7_PUBLISHER_MISMATCH = The nominated WG ''{0}'' means that the publisher should be ''{1}'' but ''{2}'' was found
|
||||
VALIDATION_HL7_PUBLISHER_MISMATCH2 = The nominated WG ''{0}'' means that the publisher should be either ''{1}''or ''{2}'' but ''{3}'' was found
|
||||
VALIDATION_HL7_WG_URL = The nominated WG ''{0}'' means that the contact url should be ''{1}'' but it was not found
|
||||
VALIDATION_HL7_PUBLISHER_MISSING = When HL7 is publishing a resource, the publisher must be provided, and for WG ''{0}'' it should be ''{1}''
|
||||
TYPE_SPECIFIC_CHECKS_DT_QTY_UCUM_ANNOTATIONS_NO_UNIT = UCUM Codes that contain human readable annotations like {0} can be misleading (e.g. they are ignored when comparing units). Best Practice is not to depend on annotations in the UCUM code, so this usage should be checked, and the Quantity.unit SHOULD contain the annotation
|
||||
|
@ -1076,7 +1080,7 @@ CDA_UNKNOWN_TEMPLATE = The CDA Template {0} is not known
|
|||
CDA_UNKNOWN_TEMPLATE_EXT = The CDA Template {0} / {1} is not known
|
||||
UNABLE_TO_DETERMINE_TYPE_CONTEXT_INV = The types could not be determined from the extension context, so the invariant can't be validated (types = {0})
|
||||
ED_CONTEXT_INVARIANT_EXPRESSION_ERROR = Error in constraint ''{0}'': {1}
|
||||
VALIDATION_VAL_PROFILE_SIGNPOST_OBS = Validate Observation against {1} profile because the {2} code {3} was found
|
||||
VALIDATION_VAL_PROFILE_SIGNPOST_OBS = Validate Observation against the {1} profile ({0}) which is required by the FHIR specification because the {2} code {3} was found
|
||||
FHIRPATH_INVALID_TYPE = The type {0} is not valid
|
||||
FHIRPATH_AS_COLLECTION = Attempt to use ''as()'' on more than one item (''{0}'', ''{1}'')
|
||||
FHIRPATH_ARITHMETIC_QTY = Error in date arithmetic: attempt to add a definite quantity duration time unit {0}
|
||||
|
|
|
@ -103,6 +103,7 @@ import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
|
|||
import org.hl7.fhir.r5.model.CodeType;
|
||||
import org.hl7.fhir.r5.model.CodeableConcept;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.r5.model.Constants;
|
||||
import org.hl7.fhir.r5.model.ContactPoint;
|
||||
import org.hl7.fhir.r5.model.DataType;
|
||||
import org.hl7.fhir.r5.model.DateTimeType;
|
||||
|
@ -2932,9 +2933,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
String regext = FHIRPathExpressionFixer.fixRegex(getRegexFromType(e.fhirType()));
|
||||
if (regext != null) {
|
||||
try {
|
||||
boolean matches = e.primitiveValue().matches(regext);
|
||||
String pt = e.primitiveValue();
|
||||
String ptFmt = null;
|
||||
if (e.getProperty().getDefinition().hasExtension(ToolingExtensions.EXT_DATE_FORMAT)) {
|
||||
ptFmt = convertForDateFormatToExternal(ToolingExtensions.readStringExtension(e.getProperty().getDefinition(), ToolingExtensions.EXT_DATE_FORMAT), pt);
|
||||
}
|
||||
boolean matches = pt.matches(regext) || (ptFmt != null && ptFmt.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;
|
||||
if (ptFmt == null) {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, matches, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX_TYPE, pt, e.fhirType(), regext) && ok;
|
||||
} else {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, matches, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX_TYPE_ALT, pt, ptFmt, 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;
|
||||
|
@ -2946,6 +2956,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return ok;
|
||||
}
|
||||
|
||||
private String convertForDateFormatToExternal(String fmt, String av) throws FHIRException {
|
||||
if ("v3".equals(fmt) || "YYYYMMDDHHMMSS.UUUU[+|-ZZzz]".equals(fmt)) {
|
||||
DateTimeType d = new DateTimeType(av);
|
||||
return d.getAsV3();
|
||||
} else
|
||||
throw new FHIRException(context.formatMessage(I18nConstants.UNKNOWN_DATE_FORMAT_, fmt));
|
||||
}
|
||||
|
||||
private boolean isCoreDefinition(StructureDefinition profile) {
|
||||
return profile.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/") && profile.getKind() != StructureDefinitionKind.LOGICAL;
|
||||
}
|
||||
|
@ -4289,7 +4307,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
if (sd != null && (sd.getTypeTail().equals(type) || sd.getUrl().equals(type)) && sd.hasSnapshot()) {
|
||||
return sd;
|
||||
}
|
||||
if (sd != null && sd.getAbstract()) {
|
||||
if (sd != null && (sd.getAbstract() || sd.getUrl().startsWith(Constants.NS_CDA_ROOT))) {
|
||||
StructureDefinition sdt = context.fetchTypeDefinition(type);
|
||||
StructureDefinition tt = sdt;
|
||||
while (tt != null) {
|
||||
|
@ -4687,10 +4705,26 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
timeTracker.sd(t);
|
||||
if (sd != null && (sd.getType().equals(type) || sd.getUrl().equals(type)) && sd.hasSnapshot())
|
||||
return sd.getSnapshot().getElement().get(0);
|
||||
if (sd != null) {
|
||||
StructureDefinition sdt = context.fetchTypeDefinition(type);
|
||||
if (inheritsFrom(sdt, sd) && sdt.hasSnapshot()) {
|
||||
return sdt.getSnapshot().getElement().get(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean inheritsFrom(StructureDefinition sdt, StructureDefinition sd) {
|
||||
while (sdt != null) {
|
||||
if (sdt == sd) {
|
||||
return true;
|
||||
}
|
||||
sdt = context.fetchResource(StructureDefinition.class, sdt.getBaseDefinition());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setAnyExtensionsAllowed(boolean anyExtensionsAllowed) {
|
||||
this.anyExtensionsAllowed = anyExtensionsAllowed;
|
||||
}
|
||||
|
@ -5544,7 +5578,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
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();
|
||||
if (warning(errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), pub != null, I18nConstants.VALIDATION_HL7_PUBLISHER_MISSING, wg, rpub)) {
|
||||
warningOrError(pub.contains("/"), errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), rpub.equals(pub), I18nConstants.VALIDATION_HL7_PUBLISHER_MISMATCH, wg, rpub, pub);
|
||||
boolean ok = rpub.equals(pub);
|
||||
if (!ok && wgd.getName2() != null) {
|
||||
ok = ("HL7 International / "+wgd.getName2()).equals(pub);
|
||||
warningOrError(pub.contains("/"), errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), ok, I18nConstants.VALIDATION_HL7_PUBLISHER_MISMATCH2, wg, rpub, "HL7 International / "+wgd.getName2(), pub);
|
||||
} else {
|
||||
warningOrError(pub.contains("/"), errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), ok, I18nConstants.VALIDATION_HL7_PUBLISHER_MISMATCH, wg, rpub, pub);
|
||||
}
|
||||
}
|
||||
warning(errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(),
|
||||
Utilities.startsWithInList( wgd.getLink(), urls), I18nConstants.VALIDATION_HL7_WG_URL, wg, wgd.getLink());
|
||||
|
@ -6024,8 +6064,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
checkMustSupport(profile, ei);
|
||||
long s = System.currentTimeMillis();
|
||||
|
||||
if (checkDefn.getType().size() == 1 && !"*".equals(checkDefn.getType().get(0).getWorkingCode()) && !"Element".equals(checkDefn.getType().get(0).getWorkingCode())
|
||||
&& !"BackboneElement".equals(checkDefn.getType().get(0).getWorkingCode())) {
|
||||
boolean hasType = checkDefn.getType().size() > 0;
|
||||
boolean isAbstract = hasType && Utilities.existsInList(checkDefn.getType().get(0).getWorkingCode(), "Element", "BackboneElement");
|
||||
boolean isChoice = checkDefn.getType().size() > 1 || (hasType && "*".equals(checkDefn.getType().get(0).getWorkingCode()));
|
||||
boolean isCDAChoice = profile.getUrl().startsWith(Constants.NS_CDA_ROOT) && ei.getElement().getExplicitType() != null;
|
||||
|
||||
if (hasType && !isChoice && !isAbstract && !isCDAChoice) {
|
||||
type = checkDefn.getType().get(0).getWorkingCode();
|
||||
typeName = type;
|
||||
if (Utilities.isAbsoluteUrl(type)) {
|
||||
|
@ -6075,12 +6119,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
profiles.add(p.getValue());
|
||||
}
|
||||
}
|
||||
} else if (checkDefn.getType().size() > 1) {
|
||||
} else if (checkDefn.getType().size() > 1 || isCDAChoice) {
|
||||
|
||||
String prefix = tail(checkDefn.getPath());
|
||||
assert typesAreAllReference(checkDefn.getType()) || checkDefn.hasRepresentation(PropertyRepresentation.TYPEATTR) || prefix.endsWith("[x]") || isResourceAndTypes(checkDefn) : "Multiple Types allowed, but name is wrong @ "+checkDefn.getPath()+": "+checkDefn.typeSummaryVB();
|
||||
assert typesAreAllReference(checkDefn.getType()) || isCDAChoice|| checkDefn.hasRepresentation(PropertyRepresentation.TYPEATTR) || prefix.endsWith("[x]") || isResourceAndTypes(checkDefn) : "Multiple Types allowed, but name is wrong @ "+checkDefn.getPath()+": "+checkDefn.typeSummaryVB();
|
||||
|
||||
if (checkDefn.hasRepresentation(PropertyRepresentation.TYPEATTR)) {
|
||||
if (isCDAChoice) {
|
||||
type = ei.getElement().getExplicitType();
|
||||
typeName = type;
|
||||
} else if (checkDefn.hasRepresentation(PropertyRepresentation.TYPEATTR)) {
|
||||
type = ei.getElement().getType();
|
||||
typeName = type;
|
||||
} else if (ei.getElement().isResource()) {
|
||||
|
@ -6476,7 +6523,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
hintPlural(errors, NO_RULE_DATE, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), count, I18nConstants.VALIDATION_VAL_PROFILE_NOCHECKMIN, profile.getVersionedUrl(), ed.getPath(), ed.getId(), ed.getSliceName(),ed.getLabel(), stack.getLiteralPath(), Integer.toString(ed.getMin()));
|
||||
else {
|
||||
if (count < ed.getMin()) {
|
||||
ok = rulePlural(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), false, count, I18nConstants.VALIDATION_VAL_PROFILE_MINIMUM, profile.getVersionedUrl(), ed.getPath(), ed.getId(), ed.getSliceName(),ed.getLabel(), stack.getLiteralPath(), Integer.toString(ed.getMin())) && ok;
|
||||
if (isObservationMagicValue(profile, ed)) {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_MINIMUM_MAGIC, ed.getSliceName(), getFixedLOINCCode(ed, profile), profile.getVersionedUrl()) && ok;
|
||||
} else {
|
||||
ok = rulePlural(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), false, count, I18nConstants.VALIDATION_VAL_PROFILE_MINIMUM, profile.getVersionedUrl(), ed.getPath(), ed.getId(), ed.getSliceName(),ed.getLabel(), stack.getLiteralPath(), Integer.toString(ed.getMin())) && ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6492,6 +6543,28 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return ok;
|
||||
}
|
||||
|
||||
private String getFixedLOINCCode(ElementDefinition ed, StructureDefinition profile) {
|
||||
if (ed.hasFixedCoding() && "http://loinc.org".equals(ed.getFixedCoding().getSystem())) {
|
||||
return ed.getFixedCoding().getCode();
|
||||
}
|
||||
SourcedChildDefinitions children = profileUtilities.getChildMap(profile, ed);
|
||||
if (children != null) {
|
||||
for (ElementDefinition t : children.getList()) {
|
||||
if (t.getPath().endsWith(".code") && t.hasFixed()) {
|
||||
return t.getFixed().primitiveValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return "??";
|
||||
}
|
||||
|
||||
private boolean isObservationMagicValue(StructureDefinition profile, ElementDefinition ed) {
|
||||
if (profile.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/") && ed.hasSliceName() && ed.getPath().equals("Observation.code.coding")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<String> assignChildren(ValidationContext valContext, List<ValidationMessage> errors, StructureDefinition profile, Element resource,
|
||||
NodeStack stack, SourcedChildDefinitions childDefinitions, List<ElementInfo> children, BooleanHolder bh) throws DefinitionException {
|
||||
// 2. assign children to a definition
|
||||
|
@ -6545,12 +6618,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
if (!unsupportedSlicing) {
|
||||
if (ei.additionalSlice && ei.definition != null) {
|
||||
if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.OPEN) ||
|
||||
ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.OPENATEND) && true /* TODO: replace "true" with condition to check that this element is at "end" */) {
|
||||
slicingHint(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, ei.line(), ei.col(), ei.getPath(), false, isProfile(slicer) || isCritical(ei.sliceInfo),
|
||||
context.formatMessage(I18nConstants.THIS_ELEMENT_DOES_NOT_MATCH_ANY_KNOWN_SLICE_,
|
||||
profile == null ? "" : "defined in the profile " + profile.getVersionedUrl()),
|
||||
context.formatMessage(I18nConstants.THIS_ELEMENT_DOES_NOT_MATCH_ANY_KNOWN_SLICE_, profile == null ? "" : context.formatMessage(I18nConstants.DEFINED_IN_THE_PROFILE) + " "+profile.getVersionedUrl()) + errorSummaryForSlicingAsHtml(ei.sliceInfo),
|
||||
errorSummaryForSlicingAsText(ei.sliceInfo));
|
||||
ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.OPENATEND) && true /* TODO: replace "true" with condition to check that this element is at "end" */) {
|
||||
if (!ignoreSlicingHint(ei.definition, profile)) {
|
||||
slicingHint(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, ei.line(), ei.col(), ei.getPath(), false, isProfile(slicer) || isCritical(ei.sliceInfo),
|
||||
context.formatMessage(I18nConstants.THIS_ELEMENT_DOES_NOT_MATCH_ANY_KNOWN_SLICE_,
|
||||
profile == null ? "" : "defined in the profile " + profile.getVersionedUrl()),
|
||||
context.formatMessage(I18nConstants.THIS_ELEMENT_DOES_NOT_MATCH_ANY_KNOWN_SLICE_, profile == null ? "" : context.formatMessage(I18nConstants.DEFINED_IN_THE_PROFILE) + " "+profile.getVersionedUrl()) + errorSummaryForSlicingAsHtml(ei.sliceInfo),
|
||||
errorSummaryForSlicingAsText(ei.sliceInfo));
|
||||
}
|
||||
} else if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.CLOSED)) {
|
||||
bh.see(rule(errors, NO_RULE_DATE, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_NOTSLICE, (profile == null ? "" : " defined in the profile " + profile.getVersionedUrl()), errorSummaryForSlicing(ei.sliceInfo)));
|
||||
}
|
||||
|
@ -6593,6 +6668,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
|
||||
|
||||
private boolean ignoreSlicingHint(ElementDefinition definition, StructureDefinition profile) {
|
||||
if (profile.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/") && "Observation.code.coding".equals(definition.getPath())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<ElementInfo> listChildren(Element element, NodeStack stack) {
|
||||
// 1. List the children, and remember their exact path (convenience)
|
||||
List<ElementInfo> children = new ArrayList<ElementInfo>();
|
||||
|
|
|
@ -270,7 +270,7 @@ public class StructureDefinitionValidator extends BaseValidator {
|
|||
// this is ok (it must have this), and there's nothing to check
|
||||
} else if (child.getName().equals("binding")) {
|
||||
if (rule(errors, "2023-05-27", IssueType.INVALID, stack.getLiteralPath(), bd.hasBinding(),
|
||||
I18nConstants.SD_OBGLIGATION_PROFILE_ILLEGAL_BINDING, id)) {
|
||||
I18nConstants.SD_OBGLIGATION_PROFILE_ILLEGAL_BINDING, id)) {
|
||||
ok = validateObligationProfileElementBinding(errors, child, stack, id, bd) && ok;
|
||||
} else {
|
||||
ok = false;
|
||||
|
@ -357,6 +357,8 @@ public class StructureDefinitionValidator extends BaseValidator {
|
|||
}
|
||||
if ("element".equals(ct) && "Element".equals(cv)) {
|
||||
warning(errors, "2023-04-23", IssueType.BUSINESSRULE, n.getLiteralPath(), false, I18nConstants.SD_CONTEXT_SHOULD_NOT_BE_ELEMENT, cv, src.getNamedChildValue("id", false));
|
||||
} else if ("fhirpath".equals(ct)) {
|
||||
warning(errors, "2023-12-05", IssueType.BUSINESSRULE, n.getLiteralPath(), !isElement(cv), I18nConstants.SD_CONTEXT_SHOULD_NOT_BE_FHIRPATH, cv, src.getNamedChildValue("id", false));
|
||||
}
|
||||
} else {
|
||||
ok = rule(errors, "2023-04-23", IssueType.INVALID, n.getLiteralPath(), false, I18nConstants.SD_NO_CONTEXT_WHEN_NOT_EXTENSION, type) && ok;
|
||||
|
@ -374,6 +376,16 @@ public class StructureDefinitionValidator extends BaseValidator {
|
|||
return ok;
|
||||
}
|
||||
|
||||
private boolean isElement(String cv) {
|
||||
String tn = cv.contains(".") ? cv.substring(0, cv.indexOf(".")) : cv;
|
||||
StructureDefinition sd = context.fetchTypeDefinition(tn);
|
||||
if (sd != null) {
|
||||
return sd.getSnapshot().getElementByPath(cv) != null;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean validateElementList(List<ValidationMessage> errors, Element elementList, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName, boolean logical, boolean constraint, String rootPath, String profileUrl, String profileType, StructureDefinition base) {
|
||||
Map<String, String> invariantMap = new HashMap<>();
|
||||
boolean ok = true;
|
||||
|
@ -457,8 +469,8 @@ public class StructureDefinitionValidator extends BaseValidator {
|
|||
ok = validateBinding(errors, binding, stack.push(binding, -1, null, null), typeCodes, snapshot, path) && ok;
|
||||
} else {
|
||||
// this is a good idea but there's plenty of cases where the rule isn't met; maybe one day it's worth investing the time to exclude these cases and bring this rule back
|
||||
// String bt = boundType(typeCodes);
|
||||
// hint(errors, UNKNOWN_DATE_TIME, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot || bt == null, I18nConstants.SD_ED_SHOULD_BIND, element.getNamedChildValue("path", false), bt);
|
||||
// String bt = boundType(typeCodes);
|
||||
// hint(errors, UNKNOWN_DATE_TIME, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot || bt == null, I18nConstants.SD_ED_SHOULD_BIND, element.getNamedChildValue("path", false), bt);
|
||||
}
|
||||
if (!typeCodes.isEmpty()) {
|
||||
if (element.hasChild("maxLength", false)) {
|
||||
|
@ -662,16 +674,16 @@ public class StructureDefinitionValidator extends BaseValidator {
|
|||
}
|
||||
|
||||
private Element getParent(List<Element> elements, Element te) {
|
||||
int i = elements.indexOf(te) - 1;
|
||||
String path = te.getNamedChildValue("path", false);
|
||||
while (i >= 0) {
|
||||
String p = elements.get(i).getNamedChildValue("path", false);
|
||||
if (path.startsWith(p+".")) {
|
||||
return elements.get(i);
|
||||
}
|
||||
i--;
|
||||
}
|
||||
return null;
|
||||
int i = elements.indexOf(te) - 1;
|
||||
String path = te.getNamedChildValue("path", false);
|
||||
while (i >= 0) {
|
||||
String p = elements.get(i).getNamedChildValue("path", false);
|
||||
if (path.startsWith(p+".")) {
|
||||
return elements.get(i);
|
||||
}
|
||||
i--;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<String> getTypesForElement(List<Element> elements, Element element, String profileType) {
|
||||
|
@ -803,9 +815,9 @@ public class StructureDefinitionValidator extends BaseValidator {
|
|||
case "Element" :return addCharacteristicsForType(set);
|
||||
case "Base" :return addCharacteristicsForType(set);
|
||||
default:
|
||||
// if (!context.getResourceNames().contains(tc)) {
|
||||
// System.out.println("Unhandled data type in addCharacteristics: "+tc);
|
||||
// }
|
||||
// if (!context.getResourceNames().contains(tc)) {
|
||||
// System.out.println("Unhandled data type in addCharacteristics: "+tc);
|
||||
// }
|
||||
return addCharacteristicsForType(set);
|
||||
}
|
||||
}
|
||||
|
@ -1060,7 +1072,7 @@ public class StructureDefinitionValidator extends BaseValidator {
|
|||
private boolean isReferenceableTarget(StructureDefinition t) {
|
||||
for (Extension ext : t.getExtensionsByUrl(ExtensionConstants.EXT_SDTYPE_CHARACTERISTICS)) {
|
||||
if (ext.hasValue()) {
|
||||
String c = ext.getValue().primitiveValue();
|
||||
String c = ext.getValue().primitiveValue();
|
||||
if ("can-be-target".equals(c)) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -53,6 +53,10 @@ public class FHIRPathExpressionFixer {
|
|||
if (expr.equals("name.matches('[A-Z]([A-Za-z0-9_]){0,254}')")) {
|
||||
return ("name.exists() implies name.matches('[A-Z]([A-Za-z0-9_]){0,254}')");
|
||||
}
|
||||
// con-3 in R4
|
||||
if (expr.equals("clinicalStatus.exists() or verificationStatus.coding.where(system='http://terminology.hl7.org/CodeSystem/condition-ver-status' and code = 'entered-in-error').exists() or category.select($this='problem-list-item').empty()")) {
|
||||
return "clinicalStatus.exists() or verificationStatus.coding.where(system='http://terminology.hl7.org/CodeSystem/condition-ver-status' and code = 'entered-in-error').exists() or category.coding.exists(system='http://terminology.hl7.org/CodeSystem/condition-category' and code ='problem-list-item').empty()";
|
||||
}
|
||||
|
||||
// R5 ballot
|
||||
if (expr.equals("url.matches('([^|#])*')")) {
|
||||
|
|
|
@ -2183,6 +2183,44 @@ v: {
|
|||
"severity" : "error",
|
||||
"error" : "The provided code 'http://snomed.info/sct#16076005' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027' (from Tx-Server)",
|
||||
"class" : "UNKNOWN",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"code" : "english"
|
||||
}, "url": "http://hl7.org/fhir/ValueSet/all-languages", "version": "4.0.1", "langs":"", "useServer":"true", "useClient":"true", "guessSystem":"true", "valueSetMode":"ALL_CHECKS", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"severity" : "error",
|
||||
"error" : "Error from http://tx-dev.fhir.org/r4: Access violation",
|
||||
"class" : "SERVER_ERROR",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"code" : "en"
|
||||
}, "url": "http://hl7.org/fhir/ValueSet/all-languages", "version": "4.0.1", "langs":"", "useServer":"true", "useClient":"true", "guessSystem":"true", "valueSetMode":"ALL_CHECKS", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"display" : "English",
|
||||
"code" : "en",
|
||||
"system" : "urn:ietf:bcp:47",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
|
|
15
pom.xml
15
pom.xml
|
@ -21,7 +21,7 @@
|
|||
<guava_version>32.0.1-jre</guava_version>
|
||||
<hapi_fhir_version>6.4.1</hapi_fhir_version>
|
||||
<validator_test_case_version>1.4.19-SNAPSHOT</validator_test_case_version>
|
||||
<jackson_version>2.15.2</jackson_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>
|
||||
<maven_surefire_version>3.0.0-M5</maven_surefire_version>
|
||||
|
@ -86,7 +86,6 @@
|
|||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.2.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
@ -297,6 +296,18 @@
|
|||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.43.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.2.13</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>1.2.13</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
|
Loading…
Reference in New Issue