Merge branch 'master' into 1509-address-line-conversion-fix

This commit is contained in:
Augustas Vilčinskas 2023-12-11 13:48:37 +01:00 committed by GitHub
commit 2cb3375cda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 467 additions and 84 deletions

View File

@ -21,3 +21,5 @@
(Unknown license) jakarta-regexp (jakarta-regexp:jakarta-regexp:1.4 - no url defined) (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. # 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) (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)

View File

@ -3,6 +3,11 @@
* Fix bug where validator doesn't actually validate web sourced input * Fix bug where validator doesn't actually validate web sourced input
* Fix narrative link validation and add id/idref validation * Fix narrative link validation and add id/idref validation
* Remove fhir-test-cases from Validator CLI JAR (#1497) (reduce size) * 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 ## Other code changes
@ -20,4 +25,5 @@
* Give kinder error message for missing param * Give kinder error message for missing param
* Fix commonmark group and bump version (#1500) * Fix commonmark group and bump version (#1500)
* Remove dep used for local testing * Remove dep used for local testing
* Bump jackson & logback versions
* Fix StringType element properties not being copied in various Address, HumanName convertors * Fix StringType element properties not being copied in various Address, HumanName convertors

View File

@ -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;
}
}

View File

@ -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.context.IWorkerContext;
import org.hl7.fhir.r5.fhirpath.TypeDetails; import org.hl7.fhir.r5.fhirpath.TypeDetails;
import org.hl7.fhir.r5.formats.FormatUtilities; 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;
import org.hl7.fhir.r5.model.ElementDefinition.PropertyRepresentation; import org.hl7.fhir.r5.model.ElementDefinition.PropertyRepresentation;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
@ -345,12 +346,13 @@ public class Property {
} }
ElementDefinition ed = definition; ElementDefinition ed = definition;
StructureDefinition sd = structure; StructureDefinition sd = structure;
boolean isCDA = isCDAElement(structure);
SourcedChildDefinitions children = profileUtilities.getChildMap(sd, ed); SourcedChildDefinitions children = profileUtilities.getChildMap(sd, ed);
String url = null; String url = null;
if (children.getList().isEmpty() || isElementWithOnlyExtension(ed, children.getList())) { if (children.getList().isEmpty() || isElementWithOnlyExtension(ed, children.getList())) {
// ok, find the right definitions // ok, find the right definitions
String t = null; String t = null;
if (ed.getType().size() == 1) if (ed.getType().size() == 1 && (statedType == null || !isCDA))
t = ed.getType().get(0).getWorkingCode(); t = ed.getType().get(0).getWorkingCode();
else if (ed.getType().size() == 0) else if (ed.getType().size() == 0)
throw new Error("types == 0, and no children found on "+getDefinition().getPath()); throw new Error("types == 0, and no children found on "+getDefinition().getPath());
@ -363,9 +365,9 @@ public class Property {
break; break;
} }
} }
if (!all) { if (!all || (isCDA && statedType != null)) {
// ok, it's polymorphic // ok, it's polymorphic
if (ed.hasRepresentation(PropertyRepresentation.TYPEATTR)) { if (ed.hasRepresentation(PropertyRepresentation.TYPEATTR) || isCDA) {
t = statedType; t = statedType;
if (t == null && ToolingExtensions.hasExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype")) if (t == null && ToolingExtensions.hasExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype"))
t = ToolingExtensions.readStringExtension(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(); url = tr.getWorkingCode();
ok = true; ok = true;
} }
if (!ok) {
sdt = findAncestor(t, sdt);
if (sdt != null) {
url = sdt.getUrl();
ok = true;
} }
if (ok) }
}
if (ok) {
break; break;
} }
if (!ok) }
if (!ok) {
throw new DefinitionException("Type '"+t+"' is not an acceptable type for '"+elementName+"' on property "+definition.getPath()); throw new DefinitionException("Type '"+t+"' is not an acceptable type for '"+elementName+"' on property "+definition.getPath());
}
} else { } else {
t = elementName.substring(tail(ed.getPath()).length() - 3); t = elementName.substring(tail(ed.getPath()).length() - 3);
if (isPrimitive(lowFirst(t))) if (isPrimitive(lowFirst(t)))
@ -421,6 +431,26 @@ public class Property {
return properties; 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 { protected List<Property> getChildProperties(TypeDetails type) throws DefinitionException {
ElementDefinition ed = definition; ElementDefinition ed = definition;
StructureDefinition sd = structure; StructureDefinition sd = structure;

View File

@ -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.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.r5.formats.FormatUtilities; import org.hl7.fhir.r5.formats.FormatUtilities;
import org.hl7.fhir.r5.formats.IParser.OutputStyle; 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.DateTimeType;
import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.ElementDefinition.PropertyRepresentation; 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); Element result = new Element(element.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd)).setFormat(FhirFormat.XML);
result.setPath(element.getLocalName()); 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.markLocation(line(element, false), col(element, false));
result.setType(element.getLocalName()); result.setType(element.getLocalName());
parseChildren(errors, path, element, result); parseChildren(errors, path, element, result);
@ -274,7 +275,7 @@ public class XmlParser extends ParserBase {
return true; 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 (policy == ValidationPolicy.EVERYTHING) {
if (empty(element) && FormatUtilities.FHIR_NS.equals(element.getNamespaceURI())) // this rule only applies to FHIR Content 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); 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"); String xsiType = element.getAttributeNS(FormatUtilities.NS_XSI, "type");
if (!Utilities.noString(xsiType)) { if (!Utilities.noString(xsiType)) {
String actualType = prop.getXmlTypeName(); String actualType = prop.getXmlTypeName();
if (!xsiType.equals(actualType)) { 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 {
logError(errors, "2023-10-12", line(element, false), col(element, false), path, IssueType.INVALID, context.formatMessage(I18nConstants.XSI_TYPE_UNNECESSARY), IssueSeverity.INFORMATION); 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 { 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); 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); Element result = new Element(base.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd)).setFormat(FhirFormat.XML).setNativeObject(base);
result.setPath(base.getLocalName()); result.setPath(base.getLocalName());
String path = "/"+pathPrefix(base.getNamespaceURI())+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()); result.setType(base.getLocalName());
parseChildren(errors, path, base, result); parseChildren(errors, path, base, result);
result.numberChildren(); result.numberChildren();
@ -469,7 +488,7 @@ public class XmlParser extends ParserBase {
} else } else
n.setType(n.getType()); 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); element.getChildren().add(n);
if (ok) { if (ok) {
if (property.isResource()) 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); Element n = new Element(name, property).markLocation(line(child, false), col(child, false)).setFormat(FhirFormat.XML).setNativeObject(child);
cgn.getChildren().add(n); cgn.getChildren().add(n);
n.setPath(element.getPath()+"."+property.getName()); 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); parseChildren(errors, npath, (org.w3c.dom.Element) child, n);
} }
} }
@ -749,6 +768,11 @@ public class XmlParser extends ParserBase {
if (hasTypeAttr(c)) if (hasTypeAttr(c))
return true; 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; return false;
} }

View File

@ -701,8 +701,9 @@ public class RenderingContext {
public GenerationRules getRules() { public GenerationRules getRules() {
return rules; return rules;
} }
public void setRules(GenerationRules rules) { public RenderingContext setRules(GenerationRules rules) {
this.rules = rules; this.rules = rules;
return this;
} }
public StandardsStatus getDefaultStandardsStatus() { public StandardsStatus getDefaultStandardsStatus() {
return defaultStandardsStatus; return defaultStandardsStatus;

View File

@ -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) { public static void setHl7WG(org.w3c.dom.Element res, String code) {
String rt = res.getNodeName(); String rt = res.getNodeName();
if (VersionUtilities.getExtendedCanonicalResourceNames("5.0.0").contains(rt)) { if (VersionUtilities.getExtendedCanonicalResourceNames("5.0.0").contains(rt)) {
@ -82,14 +88,12 @@ public class CanonicalResourceUtilities {
if (wgext == null) { if (wgext == null) {
wgext = res.getOwnerDocument().createElementNS(Constants.NS_FHIR_ROOT, "extension"); wgext = res.getOwnerDocument().createElementNS(Constants.NS_FHIR_ROOT, "extension");
wgext.setAttribute("url", ToolingExtensions.EXT_WORKGROUP); wgext.setAttribute("url", ToolingExtensions.EXT_WORKGROUP);
org.w3c.dom.Element after = XMLUtil.getFirstChild(res, "modifierExtension", "url", "identifier", "version", "status"); org.w3c.dom.Element after = XMLUtil.getLastChild(res, "id", "meta", "text", "implicitRules", "language", "text", "contained");
if (after == null) {
after = XMLUtil.getLastChild(res, "id", "meta", "text", "implicitRules", "language", "text", "contained");
if (after != null) { if (after != null) {
after = XMLUtil.getNextSibling(after); after = XMLUtil.getNextSibling(after);
} }
}
res.insertBefore(wgext, after); res.insertBefore(wgext, after);
res.insertBefore(res.getOwnerDocument().createTextNode("\n "), after);
} }
XMLUtil.clearChildren(wgext); XMLUtil.clearChildren(wgext);
org.w3c.dom.Element valueCode = res.getOwnerDocument().createElementNS(Constants.NS_FHIR_ROOT, "valueCode"); 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"); org.w3c.dom.Element pub = XMLUtil.getNamedChild(res, "publisher");
if (pub == null) { if (pub == null) {
pub = res.getOwnerDocument().createElementNS(Constants.NS_FHIR_ROOT, "publisher"); 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(pub, after);
res.insertBefore(res.getOwnerDocument().createTextNode("\n "), after);
} }
pub.setAttribute("value", "HL7 International / "+wg.getName()); 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());
} }
} }
} }

View File

@ -5,13 +5,15 @@ public class HL7WorkGroups {
public static class HL7WorkGroup { public static class HL7WorkGroup {
private String link; private String link;
private String name; private String name;
private String name2;
private String code; private String code;
protected HL7WorkGroup(String code, String name, String link) { protected HL7WorkGroup(String code, String name, String name2, String link) {
super(); super();
this.code = code; this.code = code;
this.name = name; this.name = name;
this.name2 = name2;
this.link = link; this.link = link;
} }
public String getLink() { public String getLink() {
@ -20,6 +22,9 @@ public class HL7WorkGroups {
public String getName() { public String getName() {
return name; return name;
} }
public String getName2() {
return name2;
}
public String getCode() { public String getCode() {
return code; return code;
} }
@ -27,9 +32,10 @@ public class HL7WorkGroups {
public static HL7WorkGroup find(String wg) { public static HL7WorkGroup find(String wg) {
String name = nameForWG(wg); String name = nameForWG(wg);
String name2 = name2ForWG(wg);
String url = urlForWG(wg); String url = urlForWG(wg);
if (name != null) { if (name != null) {
return new HL7WorkGroup(wg, name, url); return new HL7WorkGroup(wg, name, name2, url);
} else { } else {
return null; return null;
} }
@ -125,7 +131,7 @@ public class HL7WorkGroups {
case "sd": return "Structured Documents"; case "sd": return "Structured Documents";
case "sec": return "Security"; case "sec": return "Security";
case "soa": return "Services Oriented Architecture"; case "soa": return "Services Oriented Architecture";
case "ti": return "Vocabulary"; case "ti": return "Terminology Infrastructure";
case "tsmg": return "Terminology Services Management Group (TSMG)"; case "tsmg": return "Terminology Services Management Group (TSMG)";
case "us": return "US Realm Steering Committee"; case "us": return "US Realm Steering Committee";
case "v2": return "V2 Management Group"; case "v2": return "V2 Management Group";
@ -134,5 +140,12 @@ public class HL7WorkGroups {
return null; return null;
} }
private static String name2ForWG(String wg) {
switch (wg) {
case "ti": return "Vocabulary";
case "vocab": return "Vocabulary";
}
return null;
}
} }

View File

@ -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_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_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 = "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_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_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_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_MATCHMULTIPLE = "Validation_VAL_Profile_MatchMultiple";
public static final String VALIDATION_VAL_PROFILE_MAXIMUM = "Validation_VAL_Profile_Maximum"; 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 = "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_MULTIPLEMATCHES = "Validation_VAL_Profile_MultipleMatches";
public static final String VALIDATION_VAL_PROFILE_NOCHECKMAX = "Validation_VAL_Profile_NoCheckMax"; public static final String VALIDATION_VAL_PROFILE_NOCHECKMAX = "Validation_VAL_Profile_NoCheckMax";
public static final String VALIDATION_VAL_PROFILE_NOCHECKMIN = "Validation_VAL_Profile_NoCheckMin"; 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_NEEDED = "VALIDATION_HL7_WG_NEEDED";
public static final String VALIDATION_HL7_WG_UNKNOWN = "VALIDATION_HL7_WG_UNKNOWN"; 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_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_WG_URL = "VALIDATION_HL7_WG_URL";
public static final String VALIDATION_HL7_PUBLISHER_MISSING = "VALIDATION_HL7_PUBLISHER_MISSING"; 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"; 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 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_FOUND = "XHTML_IDREF_NOT_FOUND";
public static final String XHTML_IDREF_NOT_MULTIPLE_MATCHES = "XHTML_IDREF_NOT_MULTIPLE_MATCHES"; 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";
} }

View File

@ -75,6 +75,9 @@ public abstract class BasePackageCacheManager implements IPackageCacheManager {
} }
if (version.endsWith(".x")) { if (version.endsWith(".x")) {
version = packageClient.getLatestVersion(id, version); version = packageClient.getLatestVersion(id, version);
if (version == null) {
return null;
}
} }
InputStream stream = packageClient.fetch(id, version); InputStream stream = packageClient.fetch(id, version);

View File

@ -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 = 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_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 = 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_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_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) 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_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_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_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_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_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}) 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_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_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_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 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} 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 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_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_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_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_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}'' 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 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 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}) 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} 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_INVALID_TYPE = The type {0} is not valid
FHIRPATH_AS_COLLECTION = Attempt to use ''as()'' on more than one item (''{0}'', ''{1}'') 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} FHIRPATH_ARITHMETIC_QTY = Error in date arithmetic: attempt to add a definite quantity duration time unit {0}

View File

@ -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.CodeType;
import org.hl7.fhir.r5.model.CodeableConcept; import org.hl7.fhir.r5.model.CodeableConcept;
import org.hl7.fhir.r5.model.Coding; 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.ContactPoint;
import org.hl7.fhir.r5.model.DataType; import org.hl7.fhir.r5.model.DataType;
import org.hl7.fhir.r5.model.DateTimeType; 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())); String regext = FHIRPathExpressionFixer.fixRegex(getRegexFromType(e.fhirType()));
if (regext != null) { if (regext != null) {
try { 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) { 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) { } 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; 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; 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) { private boolean isCoreDefinition(StructureDefinition profile) {
return profile.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/") && profile.getKind() != StructureDefinitionKind.LOGICAL; 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()) { if (sd != null && (sd.getTypeTail().equals(type) || sd.getUrl().equals(type)) && sd.hasSnapshot()) {
return sd; 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 sdt = context.fetchTypeDefinition(type);
StructureDefinition tt = sdt; StructureDefinition tt = sdt;
while (tt != null) { while (tt != null) {
@ -4687,10 +4705,26 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
timeTracker.sd(t); timeTracker.sd(t);
if (sd != null && (sd.getType().equals(type) || sd.getUrl().equals(type)) && sd.hasSnapshot()) if (sd != null && (sd.getType().equals(type) || sd.getUrl().equals(type)) && sd.hasSnapshot())
return sd.getSnapshot().getElement().get(0); 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; 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) { public void setAnyExtensionsAllowed(boolean anyExtensionsAllowed) {
this.anyExtensionsAllowed = 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)) { 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(); 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)) { 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(), 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()); 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); checkMustSupport(profile, ei);
long s = System.currentTimeMillis(); long s = System.currentTimeMillis();
if (checkDefn.getType().size() == 1 && !"*".equals(checkDefn.getType().get(0).getWorkingCode()) && !"Element".equals(checkDefn.getType().get(0).getWorkingCode()) boolean hasType = checkDefn.getType().size() > 0;
&& !"BackboneElement".equals(checkDefn.getType().get(0).getWorkingCode())) { 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(); type = checkDefn.getType().get(0).getWorkingCode();
typeName = type; typeName = type;
if (Utilities.isAbsoluteUrl(type)) { if (Utilities.isAbsoluteUrl(type)) {
@ -6075,12 +6119,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
profiles.add(p.getValue()); profiles.add(p.getValue());
} }
} }
} else if (checkDefn.getType().size() > 1) { } else if (checkDefn.getType().size() > 1 || isCDAChoice) {
String prefix = tail(checkDefn.getPath()); 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(); type = ei.getElement().getType();
typeName = type; typeName = type;
} else if (ei.getElement().isResource()) { } else if (ei.getElement().isResource()) {
@ -6476,10 +6523,14 @@ 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())); 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 { else {
if (count < ed.getMin()) { if (count < ed.getMin()) {
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; 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 (ed.hasMax() && !ed.getMax().equals("*")) { if (ed.hasMax() && !ed.getMax().equals("*")) {
if (problematicPaths.contains(ed.getPath())) { if (problematicPaths.contains(ed.getPath())) {
hintPlural(errors, NO_RULE_DATE, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()), count, I18nConstants.VALIDATION_VAL_PROFILE_NOCHECKMAX, profile.getVersionedUrl(), ed.getPath(), ed.getId(), ed.getSliceName(),ed.getLabel(), stack.getLiteralPath(), ed.getMax()); hintPlural(errors, NO_RULE_DATE, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()), count, I18nConstants.VALIDATION_VAL_PROFILE_NOCHECKMAX, profile.getVersionedUrl(), ed.getPath(), ed.getId(), ed.getSliceName(),ed.getLabel(), stack.getLiteralPath(), ed.getMax());
@ -6492,6 +6543,28 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return ok; 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, public List<String> assignChildren(ValidationContext valContext, List<ValidationMessage> errors, StructureDefinition profile, Element resource,
NodeStack stack, SourcedChildDefinitions childDefinitions, List<ElementInfo> children, BooleanHolder bh) throws DefinitionException { NodeStack stack, SourcedChildDefinitions childDefinitions, List<ElementInfo> children, BooleanHolder bh) throws DefinitionException {
// 2. assign children to a definition // 2. assign children to a definition
@ -6546,11 +6619,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (ei.additionalSlice && ei.definition != null) { if (ei.additionalSlice && ei.definition != null) {
if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.OPEN) || 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" */) { 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), 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_, context.formatMessage(I18nConstants.THIS_ELEMENT_DOES_NOT_MATCH_ANY_KNOWN_SLICE_,
profile == null ? "" : "defined in the profile " + profile.getVersionedUrl()), 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), 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)); errorSummaryForSlicingAsText(ei.sliceInfo));
}
} else if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.CLOSED)) { } 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))); 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) { public List<ElementInfo> listChildren(Element element, NodeStack stack) {
// 1. List the children, and remember their exact path (convenience) // 1. List the children, and remember their exact path (convenience)
List<ElementInfo> children = new ArrayList<ElementInfo>(); List<ElementInfo> children = new ArrayList<ElementInfo>();

View File

@ -357,6 +357,8 @@ public class StructureDefinitionValidator extends BaseValidator {
} }
if ("element".equals(ct) && "Element".equals(cv)) { 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)); 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 { } else {
ok = rule(errors, "2023-04-23", IssueType.INVALID, n.getLiteralPath(), false, I18nConstants.SD_NO_CONTEXT_WHEN_NOT_EXTENSION, type) && ok; 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; 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) { 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<>(); Map<String, String> invariantMap = new HashMap<>();
boolean ok = true; 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; ok = validateBinding(errors, binding, stack.push(binding, -1, null, null), typeCodes, snapshot, path) && ok;
} else { } 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 // 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); // 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); // 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 (!typeCodes.isEmpty()) {
if (element.hasChild("maxLength", false)) { if (element.hasChild("maxLength", false)) {
@ -803,9 +815,9 @@ public class StructureDefinitionValidator extends BaseValidator {
case "Element" :return addCharacteristicsForType(set); case "Element" :return addCharacteristicsForType(set);
case "Base" :return addCharacteristicsForType(set); case "Base" :return addCharacteristicsForType(set);
default: default:
// if (!context.getResourceNames().contains(tc)) { // if (!context.getResourceNames().contains(tc)) {
// System.out.println("Unhandled data type in addCharacteristics: "+tc); // System.out.println("Unhandled data type in addCharacteristics: "+tc);
// } // }
return addCharacteristicsForType(set); return addCharacteristicsForType(set);
} }
} }

View File

@ -53,6 +53,10 @@ public class FHIRPathExpressionFixer {
if (expr.equals("name.matches('[A-Z]([A-Za-z0-9_]){0,254}')")) { 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}')"); 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 // R5 ballot
if (expr.equals("url.matches('([^|#])*')")) { if (expr.equals("url.matches('([^|#])*')")) {

View File

@ -2183,6 +2183,44 @@ v: {
"severity" : "error", "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)", "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", "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" : "", "unknown-systems" : "",
"issues" : { "issues" : {
"resourceType" : "OperationOutcome" "resourceType" : "OperationOutcome"

15
pom.xml
View File

@ -21,7 +21,7 @@
<guava_version>32.0.1-jre</guava_version> <guava_version>32.0.1-jre</guava_version>
<hapi_fhir_version>6.4.1</hapi_fhir_version> <hapi_fhir_version>6.4.1</hapi_fhir_version>
<validator_test_case_version>1.4.19-SNAPSHOT</validator_test_case_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_jupiter_version>5.9.2</junit_jupiter_version>
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version> <junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>
<maven_surefire_version>3.0.0-M5</maven_surefire_version> <maven_surefire_version>3.0.0-M5</maven_surefire_version>
@ -86,7 +86,6 @@
<dependency> <dependency>
<groupId>ch.qos.logback</groupId> <groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId> <artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
@ -297,6 +296,18 @@
<artifactId>sqlite-jdbc</artifactId> <artifactId>sqlite-jdbc</artifactId>
<version>3.43.0.0</version> <version>3.43.0.0</version>
</dependency> </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> </dependencies>
</dependencyManagement> </dependencyManagement>