Merge pull request #1779 from hapifhir/2024-10-gg-advisor

2024 10 gg advisor
This commit is contained in:
Grahame Grieve 2024-10-14 10:46:42 +08:00 committed by GitHub
commit 992ec37990
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 852 additions and 278 deletions

View File

@ -0,0 +1,37 @@
package org.hl7.fhir.convertors.conv40_50;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
import org.hl7.fhir.utilities.TextFile;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class Questionnaire40_50Test {
@Test
@DisplayName("Test r5 -> r4 Questionnaire conversion.")
public void testR5_R4() throws IOException {
InputStream r4_input = this.getClass().getResourceAsStream("/q_open_40.json");
String source = TextFile.streamToString(r4_input);
System.out.println(source);
org.hl7.fhir.r4.model.Questionnaire r4_actual = (org.hl7.fhir.r4.model.Questionnaire) new org.hl7.fhir.r4.formats.JsonParser().parse(source);
org.hl7.fhir.r5.model.Resource r5_conv = VersionConvertorFactory_40_50.convertResource(r4_actual);
org.hl7.fhir.r5.formats.JsonParser r5_parser = new org.hl7.fhir.r5.formats.JsonParser();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
r5_parser.compose(stream, r5_conv);
org.hl7.fhir.r5.model.Resource r5_streamed = (org.hl7.fhir.r5.model.Questionnaire) new org.hl7.fhir.r5.formats.JsonParser().parse(new ByteArrayInputStream(stream.toByteArray()));
org.hl7.fhir.r4.model.Resource r4_conv = VersionConvertorFactory_40_50.convertResource(r5_streamed);
assertTrue(r4_actual.equalsDeep(r4_conv), "should be the same");
}
}

View File

@ -0,0 +1,53 @@
{"resourceType": "Questionnaire",
"id": "ed364266b937bb3bd73082b1",
"item": [
{
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
"valueCodeableConcept": {
"coding": [
{
"code": "editableDropdown"
}
]
}
}
],
"id": "specimen-source",
"answerOption": [
{
"valueCoding": {
"code": "U",
"display": "Urine"
}
},
{
"valueCoding": {
"code": "B",
"display": "Blood"
}
},
{
"valueCoding": {
"code": "S",
"display": "Saliva"
}
}
],
"code": [
{
"code": "specimen-source"
}
],
"linkId": "specimen-source",
"text": "Source of specimen",
"type": "open-choice"
}
],
"name": "Test Open Choice question",
"status": "active",
"subjectType": [
"Patient"
]
}

View File

@ -294,17 +294,28 @@ public class ProfileUtilities {
public static class SourcedChildDefinitions {
private StructureDefinition source;
private List<ElementDefinition> list;
private String path;
public SourcedChildDefinitions(StructureDefinition source, List<ElementDefinition> list) {
super();
this.source = source;
this.list = list;
}
public SourcedChildDefinitions(StructureDefinition source, List<ElementDefinition> list, String path) {
super();
this.source = source;
this.list = list;
this.path = path;
}
public StructureDefinition getSource() {
return source;
}
public List<ElementDefinition> getList() {
return list;
}
public String getPath() {
return path;
}
}
public class ElementDefinitionResolution {

View File

@ -41,6 +41,7 @@ import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceDesignationComponent;
import org.hl7.fhir.r5.renderers.Renderer.RenderingStatus;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode;
@ -486,24 +487,45 @@ public class DataRenderer extends Renderer implements CodeResolver {
// }
// }
//
public void renderExtensionsInText(RenderingStatus status, XhtmlNode div, ResourceWrapper element, String sep) throws FHIRFormatError, DefinitionException, IOException {
public void renderExtensionsInText(RenderingStatus status, XhtmlNode x, ResourceWrapper element, String sep) throws FHIRFormatError, DefinitionException, IOException {
boolean first = true;
for (ResourceWrapper ext : element.extensions()) {
if (canRender(ext)) {
status.setExtensions(true);
if (first) {
first = false;
} else {
div.tx(sep);
div.tx(" ");
x.tx(sep);
x.tx(" ");
}
String lbl = getExtensionLabel(ext);
div.tx(lbl);
div.tx(": ");
renderDataType(status, div, ext.child("value"));
x.tx(lbl);
x.tx(": ");
renderDataType(status, x, ext.child("value"));
}
}
}
protected void checkRenderExtensions(RenderingStatus status, XhtmlNode x, ResourceWrapper element) throws FHIRFormatError, DefinitionException, IOException {
if (element.has("extension")) {
boolean someCanRender = false;
for (ResourceWrapper ext : element.children("extension")) {
ResourceWrapper value = ext.child("value");
if (canRender(ext) && value.isPrimitive()) {
someCanRender = true;
}
}
if (someCanRender) {
status.setExtensions(true);
x.tx(" (");
renderExtensionsInText(status, x, element, ", ");
x.tx(")");
}
}
}
// public void renderExtensionsInList(XhtmlNode div, BackboneType element, String sep) throws FHIRFormatError, DefinitionException, IOException {
// boolean first = true;
@ -742,13 +764,14 @@ public class DataRenderer extends Renderer implements CodeResolver {
}
public boolean canRenderDataType(String type) {
return context.getContextUtilities().isPrimitiveType(type) || Utilities.existsInList(type, "Annotation", "Coding", "CodeableConcept", "Identifier", "HumanName", "Address",
return context.getContextUtilities().isPrimitiveType(type) || Utilities.existsInList(type, "Annotation", "Coding", "CodeableConcept", "Identifier", "HumanName", "Address", "Dosage",
"Expression", "Money", "ContactPoint", "Quantity", "Range", "Period", "Timing", "SampledData", "Reference", "UsageContext", "ContactDetail", "Ratio", "Attachment", "CodeableReference");
}
public boolean renderDataType(RenderingStatus status, XhtmlNode x, ResourceWrapper type) throws FHIRFormatError, DefinitionException, IOException {
return renderDataType(status, null, x, type);
}
public boolean renderDataType(RenderingStatus status, XhtmlNode parent, XhtmlNode x, ResourceWrapper type) throws FHIRFormatError, DefinitionException, IOException {
if (type == null) {
return false;
@ -863,10 +886,11 @@ public class DataRenderer extends Renderer implements CodeResolver {
renderUri(status, x, type);
}
private void renderRatio(RenderingStatus status, XhtmlNode x, ResourceWrapper type) {
private void renderRatio(RenderingStatus status, XhtmlNode x, ResourceWrapper type) throws FHIRFormatError, DefinitionException, IOException {
renderQuantity(status, x, type.child("numerator"));
x.tx("/");
renderQuantity(status, x, type.child("denominator"));
renderQuantity(status, x, type.child("denominator"));
checkRenderExtensions(status, x, type);
}
private void renderAttachment(RenderingStatus status, XhtmlNode x, ResourceWrapper att) {
@ -892,6 +916,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
private void renderDateTime(RenderingStatus status, XhtmlNode x, ResourceWrapper type) throws FHIRFormatError, DefinitionException, IOException {
if (!renderPrimitiveWithNoValue(status, x, type)) {
x.tx(displayDateTime(type));
checkRenderExtensions(status, x, type);
}
}
@ -1002,7 +1027,11 @@ public class DataRenderer extends Renderer implements CodeResolver {
return true;
}
private String tail(String url) {
protected String tail(String url) {
return url.substring(url.lastIndexOf(".")+1);
}
protected String utail(String url) {
return url.contains("/") ? url.substring(url.lastIndexOf("/")+1) : url;
}
@ -1038,7 +1067,8 @@ public class DataRenderer extends Renderer implements CodeResolver {
}
}
}
}
}
checkRenderExtensions(status, x, uri);
}
protected void renderAnnotation(RenderingStatus status, XhtmlNode x, ResourceWrapper a) throws FHIRException {
@ -1270,7 +1300,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
return new CodeResolution(null, null, null, code.getText(), code.getText());
}
}
protected void renderCodingWithDetails(RenderingStatus status, XhtmlNode x, ResourceWrapper c) {
protected void renderCodingWithDetails(RenderingStatus status, XhtmlNode x, ResourceWrapper c) throws FHIRFormatError, DefinitionException, IOException {
String s = "";
if (c.has("display"))
s = context.getTranslated(c.child("display"));
@ -1296,6 +1326,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
if (c.has("version")) {
x.tx(" "+context.formatPhrase(RenderingContext.DATA_REND_VERSION, c.primitiveValue("version"), ")"));
}
checkRenderExtensions(status, x, c);
}
protected void renderCoding(RenderingStatus status, XhtmlNode x, ResourceWrapper c) {
@ -1434,7 +1465,8 @@ public class DataRenderer extends Renderer implements CodeResolver {
}
x.span(null, context.formatPhrase(RenderingContext.DATA_REND_CODES) +b.toString()).addText(s);
}
}
checkRenderExtensions(status, x, cc);
}
protected String displayIdentifier(ResourceWrapper ii) {
@ -1477,7 +1509,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
return s;
}
protected void renderIdentifier(RenderingStatus status, XhtmlNode x, ResourceWrapper ii) {
protected void renderIdentifier(RenderingStatus status, XhtmlNode x, ResourceWrapper ii) throws FHIRFormatError, DefinitionException, IOException {
if (ii.has("type")) {
ResourceWrapper type = ii.child("type");
if (type.has("text")) {
@ -1527,7 +1559,8 @@ public class DataRenderer extends Renderer implements CodeResolver {
x.tx(displayPeriod(ii.child("period")));
}
x.tx(")");
}
}
checkRenderExtensions(status, x, ii);
}
public static String displayHumanName(ResourceWrapper name) {
@ -1550,7 +1583,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
}
protected void renderHumanName(RenderingStatus status, XhtmlNode x, ResourceWrapper name) {
protected void renderHumanName(RenderingStatus status, XhtmlNode x, ResourceWrapper name) throws FHIRFormatError, DefinitionException, IOException {
StringBuilder s = new StringBuilder();
if (name.has("text"))
s.append(context.getTranslated(name.child("text")));
@ -1567,7 +1600,8 @@ public class DataRenderer extends Renderer implements CodeResolver {
if (name.has("use") && !name.primitiveValue("use").equals("usual")) {
s.append("("+context.getTranslatedCode(name.primitiveValue("use"), "http://hl7.org/fhir/name-use")+")");
}
x.addText(s.toString());
x.addText(s.toString());
checkRenderExtensions(status, x, name);
}
private String displayAddress(ResourceWrapper address) {
@ -1604,8 +1638,9 @@ public class DataRenderer extends Renderer implements CodeResolver {
return s.toString();
}
protected void renderAddress(RenderingStatus status, XhtmlNode x, ResourceWrapper address) {
x.addText(displayAddress(address));
protected void renderAddress(RenderingStatus status, XhtmlNode x, ResourceWrapper address) throws FHIRFormatError, DefinitionException, IOException {
x.addText(displayAddress(address));
checkRenderExtensions(status, x, address);
}
@ -1788,7 +1823,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
return s.toString();
}
protected void renderQuantity(RenderingStatus status, XhtmlNode x, ResourceWrapper q) {
protected void renderQuantity(RenderingStatus status, XhtmlNode x, ResourceWrapper q) throws FHIRFormatError, DefinitionException, IOException {
if (q.has("comparator"))
x.addText(q.primitiveValue("comparator"));
if (q.has("value")) {
@ -1805,7 +1840,8 @@ public class DataRenderer extends Renderer implements CodeResolver {
}
if (context.isTechnicalMode() && q.has("code")) {
x.span("background: LightGoldenRodYellow", null).tx(" "+ (context.formatPhrase(RenderingContext.DATA_REND_DETAILS, displaySystem(q.primitiveValue("system")))) +q.primitiveValue("code")+" = '"+lookupCode(q.primitiveValue("system"), null, q.primitiveValue("code"))+"')");
}
}
checkRenderExtensions(status, x, q);
}
@ -2073,7 +2109,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
st = st + "-"+rep.primitiveValue("frequencyMax");
}
if (rep.has("period")) {
st = st + " "+ (context.formatPhrase(RenderingContext.DATA_REND_PER))+rep.primitiveValue("period");
st = st + " "+ (context.formatPhrase(RenderingContext.DATA_REND_PER))+" "+rep.primitiveValue("period");
if (rep.has("periodMax"))
st = st + "-"+rep.primitiveValue("periodMax");
st = st + " "+displayTimeUnits(rep.primitiveValue("periodUnit"));

View File

@ -3,6 +3,7 @@ package org.hl7.fhir.r5.renderers;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -19,6 +20,7 @@ import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r5.renderers.StructureDefinitionRenderer.SourcedElementDefinition;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.ResourceWrapper;
import org.hl7.fhir.r5.renderers.utils.ResourceWrapper.NamedResourceWrapperList;
@ -49,7 +51,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
} else {
ElementDefinition ed = sd.getSnapshot().getElement().get(0);
containedIds.clear();
generateByProfile(status, r, sd, r, sd.getSnapshot().getElement(), ed, context.getProfileUtilities().getChildList(sd, ed), x, r.fhirType(), context.isTechnicalMode(), 0);
generateByProfile(status, r, sd, r, ed, context.getProfileUtilities().getChildList(sd, ed), x, r.fhirType(), context.isTechnicalMode(), 0);
}
} catch (Exception e) {
if (DEBUG) {
@ -159,7 +161,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
return null;
}
private void renderLeaf(RenderingStatus status, ResourceWrapper res, ResourceWrapper ew, StructureDefinition sd, ElementDefinition defn, XhtmlNode parent, XhtmlNode x, boolean title, boolean showCodeDetails, Map<String, String> displayHints, String path, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome {
private void renderLeaf(RenderingStatus status, ResourceWrapper res, ResourceWrapper ew, StructureDefinition sd, ElementDefinition defn, XhtmlNode parent, XhtmlNode x, boolean title, boolean showCodeDetails, Map<String, String> displayHints, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome {
if (ew == null)
return;
@ -173,7 +175,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
} else if (!renderDataType(status, parent, x, ew)) {
// well, we have a cell (x) to render this thing, whatever it is
// it's not a data type for which we have a built rendering, so we're going to get a list of it's renderable datatype properties, and render them in a list
SourcedChildDefinitions childDefs = context.getProfileUtilities().getChildMap(sd, defn);
// SourcedChildDefinitions childDefs = context.getProfileUtilities().getChildMap(sd, defn);
boolean first = true;
x.tx(" (");
for (ResourceWrapper child : ew.children()) {
@ -199,24 +201,24 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
}
}
private boolean displayLeaf(ResourceWrapper res, ResourceWrapper ew, ElementDefinition defn, XhtmlNode x, String name, boolean showCodeDetails, boolean allowLinks) throws FHIRException, UnsupportedEncodingException, IOException {
if (ew == null)
return false;
Map<String, String> displayHints = readDisplayHints(defn);
if (name.endsWith("[x]"))
name = name.substring(0, name.length() - 3);
if (!showCodeDetails && ew.isPrimitive() && isDefault(displayHints, ew)) {
return false;
} else if (Utilities.existsInList(ew.fhirType(), "Extension")) {
return false;
} else {
x.addText(name+": "+ displayDataType(ew));
return true;
}
}
// private boolean displayLeaf(ResourceWrapper res, ResourceWrapper ew, ElementDefinition defn, XhtmlNode x, String name, boolean showCodeDetails, boolean allowLinks) throws FHIRException, UnsupportedEncodingException, IOException {
// if (ew == null)
// return false;
//
// Map<String, String> displayHints = readDisplayHints(defn);
//
// if (name.endsWith("[x]"))
// name = name.substring(0, name.length() - 3);
//
// if (!showCodeDetails && ew.isPrimitive() && isDefault(displayHints, ew)) {
// return false;
// } else if (Utilities.existsInList(ew.fhirType(), "Extension")) {
// return false;
// } else {
// x.addText(name+": "+ displayDataType(ew));
// return true;
// }
// }
@ -250,7 +252,8 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
return code != null && (code.equals("Element") || code.equals("BackboneElement"));
}
private List<ElementDefinition> getChildrenForPath(StructureDefinition profile, List<ElementDefinition> elements, String path) throws DefinitionException {
private SourcedChildDefinitions getChildrenForPath(StructureDefinition profile, String path) throws DefinitionException {
var elements = profile.getSnapshot().getElement();
// do we need to do a name reference substitution?
for (ElementDefinition e : elements) {
if (e.getPath().equals(path) && e.hasContentReference()) {
@ -279,45 +282,45 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
}
if (results.isEmpty() && t != null && t.getType().size() == 1) {
StructureDefinition tsd = context.getWorker().fetchTypeDefinition(t.getTypeFirstRep().getWorkingCode());
return getChildrenForPath(tsd, tsd.getSnapshot().getElement(), tsd.getType());
return getChildrenForPath(tsd, tsd.getType());
}
return results;
return new SourcedChildDefinitions(profile, results, path);
}
private void generateByProfile(RenderingStatus status, ResourceWrapper res, StructureDefinition profile, ResourceWrapper e, List<ElementDefinition> allElements, ElementDefinition defn, List<ElementDefinition> children, XhtmlNode x, String path, boolean showCodeDetails, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome {
private void generateByProfile(RenderingStatus status, ResourceWrapper res, StructureDefinition profile, ResourceWrapper e, ElementDefinition defn, List<ElementDefinition> children, XhtmlNode x, String path, boolean showCodeDetails, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome {
if (children.isEmpty()) {
StructureDefinition sdt = context.getWorker().fetchTypeDefinition(e.fhirType());
if (sdt != null && (sdt.getKind() == StructureDefinitionKind.COMPLEXTYPE || sdt.getKind() == StructureDefinitionKind.PRIMITIVETYPE)) {
renderLeaf(status, res, e, profile, defn, x, x, false, showCodeDetails, readDisplayHints(defn), path, indent);
renderLeaf(status, res, e, profile, defn, x, x, false, showCodeDetails, readDisplayHints(defn), indent);
} else {
// we don't have anything to render?
}
} else {
List<NamedResourceWrapperList> pl = splitExtensions(profile, e.childrenInGroups());
for (NamedResourceWrapperList p : pl) {
generateForProperty(status, res, profile, allElements, children, x, path, showCodeDetails, indent, false, p);
generateForProperty(status, res, profile, children, x, path, showCodeDetails, indent, false, p);
}
for (NamedResourceWrapperList p : pl) {
generateForProperty(status, res, profile, allElements, children, x, path, showCodeDetails, indent, true, p);
generateForProperty(status, res, profile, children, x, path, showCodeDetails, indent, true, p);
}
}
}
private void generateForProperty(RenderingStatus status, ResourceWrapper res, StructureDefinition profile,
List<ElementDefinition> allElements, List<ElementDefinition> children, XhtmlNode x, String path,
List<ElementDefinition> children, XhtmlNode x, String path,
boolean showCodeDetails, int indent, boolean round2, NamedResourceWrapperList p)
throws UnsupportedEncodingException, IOException, EOperationOutcome {
if (!p.getValues().isEmpty()) {
ElementDefinition child = getElementDefinition(children, path+"."+p.getName());
if (child != null) {
if (!child.getBase().hasPath() || !child.getBase().getPath().startsWith("Resource.")) {
generateElementByProfile(status, res, profile, allElements, x, path, showCodeDetails, indent, p, child, round2);
generateElementByProfile(status, res, profile, x, path, showCodeDetails, indent, p, child, round2);
}
}
}
}
public void generateElementByProfile(RenderingStatus status, ResourceWrapper res, StructureDefinition profile, List<ElementDefinition> allElements, XhtmlNode x, String path,
public void generateElementByProfile(RenderingStatus status, ResourceWrapper res, StructureDefinition profile, XhtmlNode x, String path,
boolean showCodeDetails, int indent, NamedResourceWrapperList p, ElementDefinition child, boolean round2) throws UnsupportedEncodingException, IOException, EOperationOutcome {
Map<String, String> displayHints = readDisplayHints(child);
if ("DomainResource.contained".equals(child.getBase().getPath())) {
@ -336,8 +339,8 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
if (isExt) {
status.setExtensions(true);
}
List<ElementDefinition> grandChildren = getChildrenForPath(profile, allElements, path+"."+p.getName());
filterGrandChildren(grandChildren, path+"."+p.getName(), p);
SourcedChildDefinitions grandChildren = getChildrenForPath(profile, path+"."+p.getName());
filterGrandChildren(grandChildren.getList(), path+"."+p.getName(), p);
if (p.getValues().size() > 0) {
if (isSimple(child) && !isExt) {
XhtmlNode para = x.isPara() ? para = x : x.para();
@ -351,7 +354,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
if (renderAsList(child) && p.getValues().size() > 1) {
XhtmlNode list = x.ul();
for (ResourceWrapper v : p.getValues())
renderLeaf(status, res, v, profile, child, x, list.li(), false, showCodeDetails, displayHints, path, indent);
renderLeaf(status, res, v, profile, child, x, list.li(), false, showCodeDetails, displayHints, indent);
} else {
boolean first = true;
for (ResourceWrapper v : p.getValues()) {
@ -360,23 +363,23 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
} else {
para.tx(", ");
}
renderLeaf(status, res, v, profile, child, x, para, false, showCodeDetails, displayHints, path, indent);
renderLeaf(status, res, v, profile, child, x, para, false, showCodeDetails, displayHints, indent);
}
}
}
} else if (canDoTable(path, p, grandChildren, x)) {
} else if (canDoTable(path, p, grandChildren.getList(), x)) {
XhtmlNode xn = new XhtmlNode(NodeType.Element, getHeader());
xn.addText(Utilities.capitalize(Utilities.camelCase(Utilities.pluralizeMe(p.getName()))));
XhtmlNode tbl = new XhtmlNode(NodeType.Element, "table");
tbl.setAttribute("class", "grid");
XhtmlNode tr = tbl.tr();
tr.td().style("display: none").tx("-"); // work around problem with empty table rows
boolean add = addColumnHeadings(tr, grandChildren);
boolean add = addColumnHeadings(tr, grandChildren.getList());
for (ResourceWrapper v : p.getValues()) {
if (v != null) {
tr = tbl.tr();
tr.td().style("display: none").tx("*"); // work around problem with empty table rows
add = addColumnValues(status, res, tr, profile, grandChildren, v, showCodeDetails, displayHints, path, indent) || add;
add = addColumnValues(status, res, tr, profile, grandChildren.getList(), v, showCodeDetails, displayHints, indent) || add;
}
}
if (add) {
@ -393,7 +396,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
XhtmlNode para = x.para();
para.b().addText(labelforExtension(sd, p.getUrl()));
para.tx(": ");
renderLeaf(status, res, vp, profile, child, x, para, false, showCodeDetails, displayHints, path, indent);
renderLeaf(status, res, vp, profile, child, x, para, false, showCodeDetails, displayHints, indent);
} else if (!ev.isEmpty()) {
XhtmlNode bq = x.addTag("blockquote");
bq.para().b().addText(labelforExtension(sd, p.getUrl()));
@ -410,13 +413,13 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
XhtmlNode li = ul.li();
li.tx(labelForSubExtension(vv.primitiveValue("url"), sd));
li.tx(": ");
renderLeaf(status, res, vv.child("value"), sd, child, x, li, isExt, showCodeDetails, displayHints, path, indent);
renderLeaf(status, res, vv.child("value"), sd, child, x, li, isExt, showCodeDetails, displayHints, indent);
}
} else {
for (ResourceWrapper vv : ev) {
StructureDefinition ex = context.getWorker().fetchTypeDefinition("Extension");
List<ElementDefinition> children = getChildrenForPath(profile, ex.getSnapshot().getElement(), "Extension");
generateByProfile(status, res, ex, vv, allElements, child, children, bq, "Extension", showCodeDetails, indent+1);
SourcedChildDefinitions children = getChildrenForPath(ex, "Extension");
generateByProfile(status, res, ex, vv, child, children.getList(), bq, "Extension", showCodeDetails, indent+1);
}
}
}
@ -427,7 +430,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
if (v != null) {
XhtmlNode bq = x.addTag("blockquote");
bq.para().b().addText(p.getName());
generateByProfile(status, res, profile, v, allElements, child, grandChildren, bq, path+"."+p.getName(), showCodeDetails, indent+1);
generateByProfile(status, res, grandChildren.getSource(), v, child, grandChildren.getList(), bq, grandChildren.getPath(), showCodeDetails, indent+1);
}
}
}
@ -435,6 +438,29 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
}
}
//
// private String getGrandChildBase(List<ElementDefinition> grandChildren) {
// if (grandChildren == null || grandChildren.isEmpty()) {
// return null;
// }
// String[] path = grandChildren.get(0).getPath().split("\\.");
// for (int i = 1; i < grandChildren.size(); i++) {
// path = getSharedString(path, grandChildren.get(1).getPath().split("\\."));
// }
// return CommaSeparatedStringBuilder.join(".", path);
// }
//
// private String[] getSharedString(String[] path, String[] path2) {
// int m = -1;
// for (int i = 0; i < Integer.min(path.length, path2.length); i++) {
// if (path[i].equals(path2[i])) {
// m = i;
// } else {
// break;
// }
// }
// return m == -1 ? new String[0] : Arrays.copyOfRange(path, 0, m+1);
// }
private String labelForSubExtension(String url, StructureDefinition sd) {
return url;
@ -530,7 +556,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
return b;
}
private boolean addColumnValues(RenderingStatus status, ResourceWrapper res, XhtmlNode tr, StructureDefinition profile, List<ElementDefinition> grandChildren, ResourceWrapper v, boolean showCodeDetails, Map<String, String> displayHints, String path, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome {
private boolean addColumnValues(RenderingStatus status, ResourceWrapper res, XhtmlNode tr, StructureDefinition profile, List<ElementDefinition> grandChildren, ResourceWrapper v, boolean showCodeDetails, Map<String, String> displayHints, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome {
boolean b = false;
for (ElementDefinition e : grandChildren) {
List<ResourceWrapper> p = v.children(e.getPath().substring(e.getPath().lastIndexOf(".")+1));
@ -542,7 +568,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
for (ResourceWrapper vv : p) {
b = true;
td.sep(", ");
renderLeaf(status, res, vv, profile, e, td, td, false, showCodeDetails, displayHints, path, indent);
renderLeaf(status, res, vv, profile, e, td, td, false, showCodeDetails, displayHints, indent);
}
}
}
@ -656,6 +682,10 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
return path.substring(path.lastIndexOf(".")+1);
}
protected String utail(String path) {
return path.contains("/") ? path.substring(path.lastIndexOf("/")+1) : path;
}
public boolean canRender(Resource resource) {
return context.getWorker().getResourceNames().contains(resource.fhirType());
}

View File

@ -35,7 +35,6 @@ import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.utils.EOperationOutcome;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.r5.utils.XVerExtensionManager;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece;
@ -369,8 +368,10 @@ public abstract class ResourceRenderer extends DataRenderer {
} else {
x.tx("??");
}
checkRenderExtensions(status, x, type);
}
public void renderReference(ResourceWrapper res, HierarchicalTableGenerator gen, List<Piece> pieces, Reference r, boolean allowLinks) throws UnsupportedEncodingException, IOException {
if (r == null) {
pieces.add(gen.new Piece(null, "null!", null));

View File

@ -83,6 +83,42 @@ public class TerminologyClientManager {
ITerminologyClient makeClient(String id, String url, String userAgent, ToolingClientLogger logger) throws URISyntaxException;
String getVersion();
}
public class InternalLogEvent {
private String message;
private String server;
private String vs;
private String systems;
private String choices;
protected InternalLogEvent(String message, String server, String vs, String systems, String choices) {
super();
this.message = message;
this.server = server;
this.vs = vs;
this.systems = systems;
this.choices = choices;
}
protected InternalLogEvent(String message) {
super();
this.message = message;
}
public String getMessage() {
return message;
}
public String getVs() {
return vs;
}
public String getSystems() {
return systems;
}
public String getChoices() {
return choices;
}
public String getServer() {
return server;
}
}
public static final String UNRESOLVED_VALUESET = "--unknown--";
@ -93,7 +129,7 @@ public class TerminologyClientManager {
private List<TerminologyClientContext> serverList = new ArrayList<>(); // clients by server address
private Map<String, TerminologyClientContext> serverMap = new HashMap<>(); // clients by server address
private Map<String, ServerOptionList> resMap = new HashMap<>(); // client resolution list
private List<String> internalLog = new ArrayList<>();
private List<InternalLogEvent> internalLog = new ArrayList<>();
protected Parameters expParameters;
private TerminologyCache cache;
@ -124,6 +160,7 @@ public class TerminologyClientManager {
monitorServiceURL = other.monitorServiceURL;
factory = other.factory;
usage = other.usage;
internalLog = other.internalLog;
}
@ -150,6 +187,7 @@ public class TerminologyClientManager {
}
}
if (ok) {
log(vs, s, systems, choices, "Found authoritative server "+s);
return findClient(s, systems, expand);
}
}
@ -165,6 +203,7 @@ public class TerminologyClientManager {
}
}
if (ok) {
log(vs, s, systems, choices, "Found partially authoritative server "+s);
return findClient(s, systems, expand);
}
}
@ -180,6 +219,7 @@ public class TerminologyClientManager {
}
}
if (ok) {
log(vs, s, systems, choices, "Found candidate server "+s);
return findClient(s, systems, expand);
}
}
@ -194,6 +234,7 @@ public class TerminologyClientManager {
"urn:ietf:rfc:3986", "http://www.ama-assn.org/go/cpt", "urn:oid:1.2.36.1.2001.1005.17", "urn:iso:std:iso:3166",
"http://varnomen.hgvs.org", "http://unstats.un.org/unsd/methods/m49/m49.htm", "urn:iso:std:iso:4217",
"http://hl7.org/fhir/sid/ndc", "http://fhir.ohdsi.org/CodeSystem/concepts", "http://fdasis.nlm.nih.gov", "https://www.usps.com/")) {
log(vs, serverList.get(0).getAddress(), systems, choices, "Use primary server for "+uri);
return serverList.get(0);
}
}
@ -206,29 +247,37 @@ public class TerminologyClientManager {
el = getMaster().getAddress();
}
if (systems.size() == 1) {
internalLog.add(vs.getVersionedUrl()+" uses the system "+systems.toString()+" not handled by any servers. Using source @ '"+el+"'");
log(vs, el, systems, choices, "Not handled by any servers. Using source @ '"+el+"'");
} else {
internalLog.add(vs.getVersionedUrl()+" includes multiple systems "+systems.toString()+" best handled by multiple servers: "+choices.toString()+". Using source @ '"+el+"'");
log(vs, el, systems, choices, "Handled by multiple servers. Using source @ '"+el+"'");
}
return findClient(el, systems, expand);
} else {
if (systems.size() == 1) {
internalLog.add(vs.getVersionedUrl()+" uses the system "+systems.toString()+" not handled by any servers. Using master @ '"+serverList.get(0)+"'");
log(vs, serverList.get(0).getAddress(), systems, choices, "System not handled by any servers. Using primary server");
} else {
internalLog.add(vs.getVersionedUrl()+" includes multiple systems "+systems.toString()+" best handled by multiple servers: "+choices.toString()+". Using master @ '"+serverList.get(0)+"'");
log(vs, serverList.get(0).getAddress(), systems, choices, "Systems handled by multiple servers. Using primary server");
}
return findClient(serverList.get(0).getAddress(), systems, expand);
}
} else {
if (systems.size() == 1) {
internalLog.add("Request for system "+systems.toString()+" not handled by any servers. Using master @ '"+serverList.get(0)+"'");
log(vs, serverList.get(0).getAddress(), systems, choices, "System not handled by any servers. Using primary server");
} else {
internalLog.add("Request for multiple systems "+systems.toString()+" best handled by multiple servers: "+choices.toString()+". Using master @ '"+serverList.get(0)+"'");
log(vs, serverList.get(0).getAddress(), systems, choices, "Systems handled by multiple servers. Using primary server");
}
log(vs, serverList.get(0).getAddress(), systems, choices, "Fallback: primary server");
return findClient(serverList.get(0).getAddress(), systems, expand);
}
}
private void log(ValueSet vs, String server, Set<String> systems, List<ServerOptionList> choices, String message) {
String svs = (vs == null ? "null" : vs.getVersionedUrl());
String sys = systems.isEmpty() ? "--" : systems.size() == 1 ? systems.iterator().next() : systems.toString();
String sch = choices.isEmpty() ? "--" : choices.size() == 1 ? choices.iterator().next().toString() : choices.toString();
internalLog.add(new InternalLogEvent(message, server, svs, sys, sch));
}
private TerminologyClientContext findClient(String server, Set<String> systems, boolean expand) {
TerminologyClientContext client = serverMap.get(server);
if (client == null) {
@ -297,8 +346,8 @@ public class TerminologyClientManager {
return ret;
} catch (Exception e) {
String msg = "Error resolving system "+url+": "+e.getMessage()+" ("+request+")";
if (!internalLog.contains(msg)) {
internalLog.add(msg);
if (!hasMessage(msg)) {
internalLog.add(new InternalLogEvent(msg));
}
if (!monitorServiceURL.contains("tx.fhir.org")) {
e.printStackTrace();
@ -308,6 +357,15 @@ public class TerminologyClientManager {
}
private boolean hasMessage(String msg) {
for (InternalLogEvent log : internalLog) {
if (msg.equals(log.message)) {
return true;
}
}
return false;
}
public List<TerminologyClientContext> serverList() {
return serverList;
}
@ -410,10 +468,6 @@ public class TerminologyClientManager {
}
}
public List<String> getInternalLog() {
return internalLog;
}
public List<TerminologyClientContext> getServerList() {
return serverList;
}
@ -518,8 +572,8 @@ public class TerminologyClientManager {
} catch (Exception e) {
e.printStackTrace();
String msg = "Error resolving valueSet "+canonical+": "+e.getMessage()+" ("+request+")";
if (!internalLog.contains(msg)) {
internalLog.add(msg);
if (!hasMessage(msg)) {
internalLog.add(new InternalLogEvent(msg));
}
e.printStackTrace();
return null;
@ -598,8 +652,8 @@ public class TerminologyClientManager {
} catch (Exception e) {
e.printStackTrace();
String msg = "Error resolving valueSet "+canonical+": "+e.getMessage()+" ("+request+")";
if (!internalLog.contains(msg)) {
internalLog.add(msg);
if (!hasMessage(msg)) {
internalLog.add(new InternalLogEvent(msg));
}
e.printStackTrace();
return null;
@ -614,5 +668,8 @@ public class TerminologyClientManager {
}
return false;
}
public List<InternalLogEvent> getInternalLog() {
return internalLog;
}
}

View File

@ -364,7 +364,7 @@ public class TerminologyCache {
csCache.clear();
}
private void clear() throws IOException {
public void clear() throws IOException {
if (folder != null) {
Utilities.clearDirectory(folder);
}

View File

@ -80,7 +80,7 @@ public interface IResourceValidator {
IResourceValidator setFetcher(IValidatorResourceFetcher value);
IValidationPolicyAdvisor getPolicyAdvisor();
IResourceValidator setPolicyAdvisor(IValidationPolicyAdvisor advisor);
void setPolicyAdvisor(IValidationPolicyAdvisor advisor);
IValidationProfileUsageTracker getTracker();
IResourceValidator setTracker(IValidationProfileUsageTracker value);

View File

@ -16,6 +16,21 @@ import org.hl7.fhir.r5.utils.validation.constants.BindingKind;
public interface IValidationPolicyAdvisor {
/**
* Internal use, for chaining advisors
*
* @return
*/
ReferenceValidationPolicy getReferencePolicy();
/**
*
* @param path - the current path of the element
* @param messageId - the message id (from messages.properties)
* @return true if the validator should ignore the message
*/
boolean suppressMessageId(String path, String messageId);
/**
*
* @param validator
@ -169,5 +184,6 @@ public interface IValidationPolicyAdvisor {
boolean valid,
IMessagingServices msgServices,
List<ValidationMessage> messages);
}

View File

@ -514,6 +514,7 @@ public class I18nConstants {
public static final String QUESTIONNAIRE_QR_ITEM_ONLYONEI = "Questionnaire_QR_Item_OnlyOneI";
public static final String QUESTIONNAIRE_QR_ITEM_ORDER = "Questionnaire_QR_Item_Order";
public static final String QUESTIONNAIRE_QR_ITEM_STRINGNOOPTIONS = "Questionnaire_QR_Item_StringNoOptions";
public static final String QUESTIONNAIRE_QR_ITEM_STRING_IN_CODING = "QUESTIONNAIRE_QR_ITEM_STRING_IN_CODING";
public static final String QUESTIONNAIRE_QR_ITEM_TEXT = "Questionnaire_QR_Item_Text";
public static final String QUESTIONNAIRE_QR_ITEM_TIMENOOPTIONS = "Questionnaire_QR_Item_TimeNoOptions";
public static final String QUESTIONNAIRE_QR_ITEM_WRONGTYPE = "Questionnaire_QR_Item_WrongType";

View File

@ -524,6 +524,7 @@ Questionnaire_QR_Item_NoOptionsCoding = Option list has no option values of type
Questionnaire_QR_Item_NoOptionsDate = Option list has no option values of type date
Questionnaire_QR_Item_NoOptionsInteger = Option list has no option values of type integer
Questionnaire_QR_Item_NoOptionsString = Option list has no option values of type string
QUESTIONNAIRE_QR_ITEM_STRING_IN_CODING = The string value ''{0}'' matches an entry in the list of valid Codings, so this is probably an error
Questionnaire_QR_Item_NoOptionsTime = Option list has no option values of type time
Questionnaire_QR_Item_NoString = The string {0} is not a valid option
Questionnaire_QR_Item_NoTime = The time {0} is not a valid option

View File

@ -69,9 +69,11 @@ import org.hl7.fhir.r5.utils.XVerExtensionManager;
import org.hl7.fhir.r5.utils.XVerExtensionManager.XVerExtensionStatus;
import org.hl7.fhir.r5.utils.validation.IMessagingServices;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier.IValidationContextResourceLoader;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.FhirPublication;
import org.hl7.fhir.utilities.StandardsStatus;
@ -85,6 +87,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.hl7.fhir.validation.cli.utils.ValidationLevel;
import org.hl7.fhir.validation.instance.advisor.BasePolicyAdvisorForFullValidation;
import org.hl7.fhir.validation.instance.utils.IndexedElement;
import org.hl7.fhir.validation.instance.utils.NodeStack;
@ -165,25 +168,31 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
protected final String BUNDLE = "Bundle";
protected final String LAST_UPDATED = "lastUpdated";
protected String sessionId;
protected BaseValidator parent;
protected Source source;
protected IWorkerContext context;
protected ValidationTimeTracker timeTracker = new ValidationTimeTracker();
protected XVerExtensionManager xverManager;
protected List<TrackedLocationRelatedMessage> trackedMessages = new ArrayList<>();
protected List<ValidationMessage> messagesToRemove = new ArrayList<>();
protected ValidationLevel level = ValidationLevel.HINTS;
protected Coding jurisdiction;
protected boolean allowExamples;
protected boolean forPublication;
protected boolean debug;
protected boolean warnOnDraftOrExperimental;
protected Set<String> statusWarnings = new HashSet<>();
protected BestPracticeWarningLevel bpWarnings = BestPracticeWarningLevel.Warning;
protected String sessionId = Utilities.makeUuidLC();
protected List<UsageContext> usageContexts = new ArrayList<UsageContext>();
protected ValidationOptions baseOptions = new ValidationOptions(FhirPublication.R5);
protected IValidatorResourceFetcher fetcher;
protected IValidationPolicyAdvisor policyAdvisor;
// these two related to removing warnings on extensible bindings in structures that have derivatives that replace their bindings
protected List<TrackedLocationRelatedMessage> trackedMessages = new ArrayList<>();
protected List<ValidationMessage> messagesToRemove = new ArrayList<>();
// don't repeatedly raise the same warnings all the time
protected Set<String> statusWarnings = new HashSet<>();
protected Source source; // @configuration
protected ValidationLevel level = ValidationLevel.HINTS; // @configuration
protected Coding jurisdiction; // @configuration
protected boolean allowExamples; // @configuration
protected boolean forPublication; // @configuration
protected boolean debug; // @configuration
protected boolean warnOnDraftOrExperimental; // @configuration
protected BestPracticeWarningLevel bpWarnings = BestPracticeWarningLevel.Warning; // @configuration
protected List<UsageContext> usageContexts = new ArrayList<UsageContext>(); // @configuration
protected ValidationOptions baseOptions = new ValidationOptions(FhirPublication.R5); // @configuration
public BaseValidator(IWorkerContext context, XVerExtensionManager xverManager, boolean debug) {
super();
@ -193,6 +202,8 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
this.xverManager = new XVerExtensionManager(context);
}
this.debug = debug;
sessionId = Utilities.makeUuidLC();
policyAdvisor = new BasePolicyAdvisorForFullValidation(ReferenceValidationPolicy.CHECK_VALID);
urlRegex = Constants.URI_REGEX_XVER.replace("$$", CommaSeparatedStringBuilder.join("|", context.getResourceNames()));
}
@ -217,6 +228,8 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
this.usageContexts.addAll(parent.usageContexts);
this.baseOptions = parent.baseOptions;
this.fetcher = parent.fetcher;
this.sessionId = parent.sessionId;
this.policyAdvisor = parent.policyAdvisor;
}
private boolean doingLevel(IssueSeverity error) {
@ -261,75 +274,22 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
protected String urlRegex;
/**
* Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
@Deprecated
protected boolean fail(List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String msg) {
if (!thePass && doingErrors()) {
addValidationMessage(errors, ruleDate, type, line, col, path, msg, IssueSeverity.FATAL, null);
private boolean suppressMsg(String path, String theMessage) {
if (policyAdvisor == null) {
return false;
} else {
return policyAdvisor.suppressMessageId(path, theMessage);
}
return thePass;
}
protected boolean fail(List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass && doingErrors()) {
if (!thePass && doingErrors() && !suppressMsg(path, theMessage)) {
String msg = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, line, col, path, msg, IssueSeverity.FATAL, theMessage);
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
@Deprecated
protected boolean fail(List<ValidationMessage> errors, String ruleDate, IssueType type, List<String> pathParts, boolean thePass, String msg) {
if (!thePass && doingErrors()) {
String path = toPath(pathParts);
addValidationMessage(errors, ruleDate, type, -1, -1, path, msg, IssueSeverity.FATAL, null);
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
@Deprecated
protected boolean fail(List<ValidationMessage> errors, String ruleDate, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass && doingErrors()) {
String path = toPath(pathParts);
addValidationMessage(errors, ruleDate, type, -1, -1, path, context.formatMessage(theMessage, theMessageArguments), IssueSeverity.FATAL, theMessage);
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
@Deprecated
protected boolean fail(List<ValidationMessage> errors, String ruleDate, IssueType type, String path, boolean thePass, String msg) {
if (!thePass && doingErrors()) {
addValidationMessage(errors, ruleDate, type, -1, -1, path, msg, IssueSeverity.FATAL, null);
}
return thePass;
}
//TODO: i18n
protected boolean grammarWord(String w) {
return w.equals("and") || w.equals("or") || w.equals("a") || w.equals("the") || w.equals("for") || w.equals("this") || w.equals("that") || w.equals("of");
@ -343,7 +303,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean hint(List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String msg) {
if (!thePass && doingHints()) {
if (!thePass && doingHints() && !suppressMsg(path, msg)) {
String message = context.formatMessage(msg);
addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.INFORMATION, msg);
}
@ -358,7 +318,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean hintInv(List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String msg, String invId) {
if (!thePass && doingHints()) {
if (!thePass && doingHints() && !suppressMsg(path, invId)) {
String message = context.formatMessage(msg);
addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.INFORMATION, msg).setInvId(invId);
}
@ -397,7 +357,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean hint(List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass && doingHints()) {
if (!thePass && doingHints() && !suppressMsg(path, theMessage)) {
String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.INFORMATION, theMessage);
}
@ -405,7 +365,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
}
protected boolean hintPlural(List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, int num, String theMessage, Object... theMessageArguments) {
if (!thePass && doingHints()) {
if (!thePass && doingHints() && !suppressMsg(path, theMessage)) {
String message = context.formatMessagePlural(num, theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.INFORMATION, theMessage);
}
@ -418,7 +378,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
}
protected boolean txHint(List<ValidationMessage> errors, String ruleDate, String txLink, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass && doingHints()) {
if (!thePass && doingHints() && !suppressMsg(path, theMessage)) {
String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.INFORMATION, Source.TerminologyEngine, theMessage).setTxLink(txLink);
}
@ -433,7 +393,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean hint(List<ValidationMessage> errors, String ruleDate, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass && doingHints()) {
if (!thePass && doingHints() && !suppressMsg(CommaSeparatedStringBuilder.join(".", pathParts), theMessage)) {
String path = toPath(pathParts);
String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, -1, -1, path, message, IssueSeverity.INFORMATION, theMessage);
@ -449,7 +409,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean hint(List<ValidationMessage> errors, String ruleDate, IssueType type, String path, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass && doingHints()) {
if (!thePass && doingHints() && !suppressMsg(path, theMessage)) {
String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, -1, -1, path, message, IssueSeverity.INFORMATION, null);
}
@ -464,7 +424,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean rule(List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass && doingErrors()) {
if (!thePass && doingErrors() && !suppressMsg(path, theMessage)) {
String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.ERROR, theMessage);
}
@ -472,7 +432,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
}
protected boolean ruleInv(List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String theMessage, String invId, Object... theMessageArguments) {
if (!thePass && doingErrors()) {
if (!thePass && doingErrors() && !suppressMsg(path, theMessage)) {
String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.ERROR, invId).setInvId(invId);
}
@ -480,7 +440,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
}
protected boolean rule(List<ValidationMessage> errors, String ruleDate, IssueType type, NodeStack stack, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass && doingErrors()) {
if (!thePass && doingErrors() && !suppressMsg(stack.getLiteralPath(), theMessage)) {
String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, stack.line(), stack.col(), stack.getLiteralPath(), message, IssueSeverity.ERROR, theMessage);
}
@ -492,7 +452,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
}
protected boolean rulePlural(List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, int num, String theMessage, Object... theMessageArguments) {
if (!thePass && doingErrors()) {
if (!thePass && doingErrors() && !suppressMsg(path, theMessage)) {
String message = context.formatMessagePlural(num, theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.ERROR, theMessage);
}
@ -500,7 +460,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
}
protected boolean txRule(List<ValidationMessage> errors, String ruleDate, String txLink, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass && doingErrors()) {
if (!thePass && doingErrors() && !suppressMsg(path, theMessage)) {
String message = context.formatMessage(theMessage, theMessageArguments);
ValidationMessage vm = new ValidationMessage(Source.TerminologyEngine, type, line, col, path, message, IssueSeverity.ERROR).setMessageId(idForMessage(theMessage, message));
vm.setRuleDate(ruleDate);
@ -538,7 +498,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean rule(List<ValidationMessage> errors, String ruleDate, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass && doingErrors()) {
if (!thePass && doingErrors() && !suppressMsg(CommaSeparatedStringBuilder.join(".", pathParts), theMessage)) {
String path = toPath(pathParts);
String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, -1, -1, path, message, IssueSeverity.ERROR, theMessage);
@ -556,7 +516,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
protected boolean rule(List<ValidationMessage> errors, String ruleDate, IssueType type, String path, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass && doingErrors()) {
if (!thePass && doingErrors() && !suppressMsg(path, theMessage)) {
String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, -1, -1, path, message, IssueSeverity.ERROR, theMessage);
}
@ -564,7 +524,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
}
protected boolean rulePlural(List<ValidationMessage> errors, String ruleDate, IssueType type, String path, boolean thePass, int num, String theMessage, Object... theMessageArguments) {
if (!thePass && doingErrors()) {
if (!thePass && doingErrors() && !suppressMsg(path, theMessage)) {
String message = context.formatMessagePlural(num, theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, -1, -1, path, message, IssueSeverity.ERROR, theMessage);
}
@ -630,7 +590,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean warning(List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) {
if (!thePass && doingWarnings()) {
if (!thePass && doingWarnings() && !suppressMsg(path, msg)) {
String nmsg = context.formatMessage(msg, theMessageArguments);
IssueSeverity severity = IssueSeverity.WARNING;
addValidationMessage(errors, ruleDate, type, line, col, path, nmsg, severity, msg);
@ -640,7 +600,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
}
protected boolean warning(List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, String id, boolean thePass, String msg, Object... theMessageArguments) {
if (!thePass && doingWarnings()) {
if (!thePass && doingWarnings() && !suppressMsg(path, msg)) {
String nmsg = context.formatMessage(msg, theMessageArguments);
IssueSeverity severity = IssueSeverity.WARNING;
addValidationMessage(errors, ruleDate, type, line, col, path, nmsg, severity, id);
@ -650,7 +610,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
}
protected boolean warningInv(List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String msg, String invId, Object... theMessageArguments) {
if (!thePass && doingWarnings()) {
if (!thePass && doingWarnings() && !suppressMsg(path, invId)) {
String nmsg = context.formatMessage(msg, theMessageArguments);
IssueSeverity severity = IssueSeverity.WARNING;
String id = idForMessage(msg, nmsg);
@ -669,7 +629,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
}
protected boolean warningPlural(List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, int num, String msg, Object... theMessageArguments) {
if (!thePass && doingWarnings()) {
if (!thePass && doingWarnings() && !suppressMsg(path, msg)) {
String nmsg = context.formatMessagePlural(num, msg, theMessageArguments);
IssueSeverity severity = IssueSeverity.WARNING;
addValidationMessage(errors, ruleDate, type, line, col, path, nmsg, severity, msg);
@ -711,7 +671,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean txWarning(List<ValidationMessage> errors, String ruleDate, String txLink, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) {
if (!thePass && doingWarnings()) {
if (!thePass && doingWarnings() && !suppressMsg(path, msg)) {
String nmsg = context.formatMessage(msg, theMessageArguments);
ValidationMessage vmsg = new ValidationMessage(Source.TerminologyEngine, type, line, col, path, nmsg, IssueSeverity.WARNING).setTxLink(txLink).setMessageId(idForMessage(msg, nmsg));
vmsg.setRuleDate(ruleDate);
@ -744,7 +704,9 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
vmsg.setMessageId(issue.getExtensionString(ToolingExtensions.EXT_ISSUE_MSG_ID));
}
errors.add(vmsg);
if (!suppressMsg(path, vmsg.getMessageId())) {
errors.add(vmsg);
}
return vmsg;
}
@ -760,7 +722,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean txWarningForLaterRemoval(Object location, List<ValidationMessage> errors, String ruleDate, String txLink, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) {
if (!thePass && doingWarnings()) {
if (!thePass && doingWarnings() && !suppressMsg(path, msg)) {
String nmsg = context.formatMessage(msg, theMessageArguments);
ValidationMessage vmsg = new ValidationMessage(Source.TerminologyEngine, type, line, col, path, nmsg, IssueSeverity.WARNING).setTxLink(txLink).setMessageId(msg);
vmsg.setRuleDate(ruleDate);
@ -789,7 +751,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
}
protected boolean warningOrError(boolean isError, List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) {
if (!thePass) {
if (!thePass && !suppressMsg(path, msg)) {
String nmsg = context.formatMessage(msg, theMessageArguments);
IssueSeverity lvl = isError ? IssueSeverity.ERROR : IssueSeverity.WARNING;
if (doingLevel(lvl)) {
@ -805,7 +767,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
}
protected boolean hintOrError(boolean isError, List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) {
if (!thePass) {
if (!thePass && !suppressMsg(path, msg)) {
String nmsg = context.formatMessage(msg, theMessageArguments);
IssueSeverity lvl = isError ? IssueSeverity.ERROR : IssueSeverity.INFORMATION;
if (doingLevel(lvl)) {
@ -824,7 +786,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean warning(List<ValidationMessage> errors, String ruleDate, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass && doingWarnings()) {
if (!thePass && doingWarnings() && !suppressMsg(CommaSeparatedStringBuilder.join(".", pathParts), theMessage)) {
String path = toPath(pathParts);
String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, -1, -1, path, message, IssueSeverity.WARNING, theMessage);
@ -840,7 +802,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean warning(List<ValidationMessage> errors, String ruleDate, IssueType type, String path, boolean thePass, String msg, Object... theMessageArguments) {
if (!thePass && doingWarnings()) {
if (!thePass && doingWarnings() && !suppressMsg(path, msg)) {
String message = context.formatMessage(msg, theMessageArguments);
addValidationMessage(errors, ruleDate, type, -1, -1, path, message, IssueSeverity.WARNING, null);
}
@ -855,7 +817,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean warningOrHint(List<ValidationMessage> errors, String ruleDate, IssueType type, String path, boolean thePass, boolean warning, String msg, Object... theMessageArguments) {
if (!thePass) {
if (!thePass && !suppressMsg(path, msg)) {
String message = context.formatMessage(msg, theMessageArguments);
IssueSeverity lvl = warning ? IssueSeverity.WARNING : IssueSeverity.INFORMATION;
if (doingLevel(lvl)) {
@ -873,7 +835,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean warningHtml(List<ValidationMessage> errors, String ruleDate, IssueType type, String path, boolean thePass, String msg, String html) {
if (!thePass && doingWarnings()) {
if (!thePass && doingWarnings() && !suppressMsg(path, msg)) {
addValidationMessage(errors, ruleDate, type, path, msg, html, IssueSeverity.WARNING, null);
}
return thePass;
@ -887,7 +849,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean warningHtml(List<ValidationMessage> errors, String ruleDate, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) {
if (!thePass && doingWarnings()) {
if (!thePass && doingWarnings() && !suppressMsg(path, msg)) {
String nmsg = context.formatMessage(msg, theMessageArguments);
addValidationMessage(errors, ruleDate, type, path, nmsg, html, IssueSeverity.WARNING, msg);
}
@ -903,7 +865,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean suppressedwarning(List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) {
if (!thePass && doingWarnings()) {
if (!thePass && doingWarnings() && !suppressMsg(path, msg)) {
String nmsg = context.formatMessage(msg, theMessageArguments);
addValidationMessage(errors, ruleDate, type, line, col, path, nmsg, IssueSeverity.INFORMATION, msg);
}
@ -919,7 +881,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean suppressedwarning(List<ValidationMessage> errors, String ruleDate, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass && doingWarnings()) {
if (!thePass && doingWarnings() && !suppressMsg(CommaSeparatedStringBuilder.join(".", pathParts), theMessage)) {
String path = toPath(pathParts);
String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, -1, -1, path, message, IssueSeverity.INFORMATION, theMessage);
@ -974,7 +936,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean suppressedwarning(List<ValidationMessage> errors, String ruleDate, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) {
if (!thePass && doingWarnings()) {
if (!thePass && doingWarnings() && !suppressMsg(path, msg)) {
String nmsg = context.formatMessage(msg, theMessageArguments);
addValidationMessage(errors, ruleDate, type, path, nmsg, html, IssueSeverity.INFORMATION, msg);
}
@ -1681,4 +1643,17 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
return false;
}
public IValidationPolicyAdvisor getPolicyAdvisor() {
return policyAdvisor;
}
public void setPolicyAdvisor(IValidationPolicyAdvisor advisor) {
if (advisor == null) {
throw new Error("Cannot set advisor to null");
}
this.policyAdvisor = advisor;
}
}

View File

@ -1288,4 +1288,14 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
definition, structure, resource, valid, msgServices, messages);
}
@Override
public boolean suppressMessageId(String path, String messageId) {
return policyAdvisor.suppressMessageId(path, messageId);
}
@Override
public ReferenceValidationPolicy getReferencePolicy() {
return ReferenceValidationPolicy.IGNORE;
}
}

View File

@ -376,6 +376,9 @@ public class ValidatorCli {
((StandaloneTask) cliTask).executeTask(cliContext,params,tt,tts);
}
if (cliContext.getAdvisorFile() != null) {
System.out.println("Note: Some validation issues might be hidden by the advisor settings in the file "+cliContext.getAdvisorFile());
}
System.out.println("Done. " + tt.report()+". Max Memory = "+Utilities.describeSize(Runtime.getRuntime().maxMemory()));
SystemExitManager.finish();
}

View File

@ -132,6 +132,12 @@ public class CliContext {
@JsonProperty("showTimes")
private boolean showTimes = false;
@JsonProperty("showTerminologyRouting")
private boolean showTerminologyRouting = false;
@JsonProperty("clearTxCache")
private boolean clearTxCache = false;
@JsonProperty("locale")
private String locale = Locale.ENGLISH.toLanguageTag();
@ -172,6 +178,9 @@ public class CliContext {
@JsonProperty("noExperimentalContent")
private boolean noExperimentalContent;
@JsonProperty("advisorFile")
private String advisorFile;
@JsonProperty("baseEngine")
public String getBaseEngine() {
return baseEngine;
@ -754,6 +763,22 @@ public class CliContext {
this.showTimes = showTimes;
}
public boolean isShowTerminologyRouting() {
return showTerminologyRouting;
}
public void setShowTerminologyRouting(boolean showTerminologyRouting) {
this.showTerminologyRouting = showTerminologyRouting;
}
public boolean isClearTxCache() {
return clearTxCache;
}
public void setClearTxCache(boolean clearTxCache) {
this.clearTxCache = clearTxCache;
}
public String getOutputStyle() {
return outputStyle;
}
@ -852,6 +877,7 @@ public class CliContext {
Objects.equals(watchScanDelay, that.watchScanDelay) &&
Objects.equals(unknownCodeSystemsCauseErrors, that.unknownCodeSystemsCauseErrors) &&
Objects.equals(noExperimentalContent, that.noExperimentalContent) &&
Objects.equals(advisorFile, that.advisorFile) &&
Objects.equals(watchSettleTime, that.watchSettleTime) ;
}
@ -860,7 +886,7 @@ public class CliContext {
return Objects.hash(baseEngine, doNative, extensions, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching,
noExtensibleBindingMessages, noInvariants, displayWarnings, wantInvariantsInMessages, map, output, outputSuffix, htmlOutput, txServer, sv, txLog, txCache, mapLog, lang, srcLang, tgtLang, fhirpath, snomedCT,
targetVer, igs, questionnaireMode, level, profiles, sources, inputs, mode, locale, locations, crumbTrails, showMessageIds, forPublication, showTimes, allowExampleUrls, outputStyle, jurisdiction, noUnicodeBiDiControlChars,
watchMode, watchScanDelay, watchSettleTime, bestPracticeLevel, unknownCodeSystemsCauseErrors, noExperimentalContent, htmlInMarkdownCheck, allowDoubleQuotesInFHIRPath, checkIPSCodes);
watchMode, watchScanDelay, watchSettleTime, bestPracticeLevel, unknownCodeSystemsCauseErrors, noExperimentalContent, advisorFile, htmlInMarkdownCheck, allowDoubleQuotesInFHIRPath, checkIPSCodes);
}
@Override
@ -922,6 +948,7 @@ public class CliContext {
", watchScanDelay=" + watchScanDelay +
", unknownCodeSystemsCauseErrors=" + unknownCodeSystemsCauseErrors +
", noExperimentalContent=" + noExperimentalContent +
", advisorFile=" + advisorFile +
'}';
}
@ -1002,5 +1029,15 @@ public class CliContext {
this.noExperimentalContent = noExperimentalContent;
}
@JsonProperty("advisorFile")
public String getAdvisorFile() {
return advisorFile;
}
@JsonProperty("advisorFile")
public void setAdvisorFile(String advisorFile) {
this.advisorFile = advisorFile;
}
}

View File

@ -17,12 +17,14 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.context.IWorkerContextManager;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.elementmodel.Element.SpecialElement;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
import org.hl7.fhir.r5.utils.validation.IMessagingServices;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
@ -41,13 +43,14 @@ import org.hl7.fhir.utilities.json.model.JsonObject;
import org.hl7.fhir.utilities.json.parser.JsonParser;
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
import org.hl7.fhir.utilities.npm.NpmPackage;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.validation.cli.utils.Common;
import org.hl7.fhir.validation.instance.advisor.BasePolicyAdvisorForFullValidation;
import javax.annotation.Nonnull;
public class StandAloneValidatorFetcher extends BasePolicyAdvisorForFullValidation implements IValidatorResourceFetcher, IValidationPolicyAdvisor, IWorkerContextManager.ICanonicalResourceLocator {
public class StandAloneValidatorFetcher implements IValidatorResourceFetcher, IValidationPolicyAdvisor, IWorkerContextManager.ICanonicalResourceLocator {
List<String> mappingsUris = new ArrayList<>();
private FilesystemPackageCacheManager pcm;
@ -56,12 +59,13 @@ public class StandAloneValidatorFetcher extends BasePolicyAdvisorForFullValidati
private Map<String, Boolean> urlList = new HashMap<>();
private Map<String, String> pidList = new HashMap<>();
private Map<String, NpmPackage> pidMap = new HashMap<>();
private IValidationPolicyAdvisor policyAdvisor;
public StandAloneValidatorFetcher(FilesystemPackageCacheManager pcm, IWorkerContext context, IPackageInstaller installer) {
super(ReferenceValidationPolicy.IGNORE);
this.pcm = pcm;
this.context = context;
this.installer = installer;
this.policyAdvisor = new BasePolicyAdvisorForFullValidation(ReferenceValidationPolicy.IGNORE);
}
@Override
@ -297,4 +301,55 @@ public class StandAloneValidatorFetcher extends BasePolicyAdvisorForFullValidati
return new HashSet<>();
}
@Override
public boolean suppressMessageId(String path, String messageId) {
return policyAdvisor.suppressMessageId(path, messageId);
}
@Override
public ContainedReferenceValidationPolicy policyForContained(IResourceValidator validator, Object appContext,
StructureDefinition structure, ElementDefinition element, String containerType, String containerId,
SpecialElement containingResourceType, String path, String url) {
return policyAdvisor.policyForContained(validator, appContext, structure, element, containerType, containerId, containingResourceType, path, url);
}
@Override
public EnumSet<ResourceValidationAction> policyForResource(IResourceValidator validator, Object appContext,
StructureDefinition type, String path) {
return policyAdvisor.policyForResource(validator, appContext, type, path);
}
@Override
public EnumSet<ElementValidationAction> policyForElement(IResourceValidator validator, Object appContext,
StructureDefinition structure, ElementDefinition element, String path) {
return policyAdvisor.policyForElement(validator, appContext, structure, element, path);
}
@Override
public EnumSet<CodedContentValidationAction> policyForCodedContent(IResourceValidator validator, Object appContext,
String stackPath, ElementDefinition definition, StructureDefinition structure, BindingKind kind,
AdditionalBindingPurpose purpose, ValueSet valueSet, List<String> systems) {
return policyAdvisor.policyForCodedContent(validator, appContext, stackPath, definition, structure, kind, purpose, valueSet, systems);
}
@Override
public List<StructureDefinition> getImpliedProfilesForResource(IResourceValidator validator, Object appContext,
String stackPath, ElementDefinition definition, StructureDefinition structure, Element resource, boolean valid,
IMessagingServices msgServices, List<ValidationMessage> messages) {
return policyAdvisor.getImpliedProfilesForResource(validator, appContext, stackPath, definition, structure, resource, valid, msgServices, messages);
}
@Override
public ReferenceValidationPolicy getReferencePolicy() {
return policyAdvisor.getReferencePolicy();
}
public IValidationPolicyAdvisor getPolicyAdvisor() {
return policyAdvisor;
}
public void setPolicyAdvisor(IValidationPolicyAdvisor policyAdvisor) {
this.policyAdvisor = policyAdvisor;
}
}

View File

@ -40,6 +40,7 @@ import org.hl7.fhir.r5.renderers.spreadsheets.ConceptMapSpreadsheetGenerator;
import org.hl7.fhir.r5.renderers.spreadsheets.StructureDefinitionSpreadsheetGenerator;
import org.hl7.fhir.r5.renderers.spreadsheets.ValueSetSpreadsheetGenerator;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.InternalLogEvent;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache;
import org.hl7.fhir.utilities.FhirPublication;
import org.hl7.fhir.utilities.SystemExitManager;
@ -69,6 +70,8 @@ import org.hl7.fhir.validation.cli.renderers.ValidationOutputRenderer;
import org.hl7.fhir.validation.cli.utils.Common;
import org.hl7.fhir.validation.cli.utils.EngineMode;
import org.hl7.fhir.validation.cli.utils.VersionSourceInformation;
import org.hl7.fhir.validation.instance.advisor.JsonDrivenPolicyAdvisor;
import org.hl7.fhir.validation.instance.advisor.TextDrivenPolicyAdvisor;
public class ValidationService {
@ -155,6 +158,7 @@ public class ValidationService {
if (request.getCliContext().isShowTimes()) {
response.getValidationTimes().put(fileToValidate.getFileName(), validatedFragments.getValidationTime());
}
}
}
@ -289,6 +293,19 @@ public class ValidationService {
TextFile.stringToFile(html, cliContext.getHtmlOutput());
System.out.println("HTML Summary in " + cliContext.getHtmlOutput());
}
if (cliContext.isShowTerminologyRouting()) {
System.out.println("");
System.out.println("Terminology Routing Dump ---------------------------------------");
if (validator.getContext().getTxClientManager().getInternalLog().isEmpty()) {
System.out.println("(nothing happened)");
} else {
for (InternalLogEvent log : validator.getContext().getTxClientManager().getInternalLog()) {
System.out.println(log.getMessage()+" -> "+log.getServer()+" (for VS "+log.getVs()+" with systems '"+log.getSystems()+"', choices = '"+log.getChoices()+"')");
}
}
validator.getContext().getTxClientManager().getInternalLog().clear();
}
}
if (watch != ValidatorWatchMode.NONE) {
if (statusNeeded) {
@ -545,6 +562,10 @@ public class ValidationService {
System.out.println(" No Terminology Cache");
} else {
System.out.println(" Terminology Cache at "+validationEngine.getContext().getTxCache().getFolder());
if (cliContext.isClearTxCache()) {
System.out.println(" Terminology Cache Entries Cleaned out");
validationEngine.getContext().getTxCache().clear();
}
}
System.out.print(" Get set... ");
validationEngine.setQuestionnaireMode(cliContext.getQuestionnaireMode());
@ -584,6 +605,13 @@ public class ValidationService {
validationEngine.setFetcher(fetcher);
validationEngine.getContext().setLocator(fetcher);
validationEngine.setPolicyAdvisor(fetcher);
if (cliContext.getAdvisorFile() != null) {
if (cliContext.getAdvisorFile().endsWith(".json")) {
fetcher.setPolicyAdvisor(new JsonDrivenPolicyAdvisor(fetcher.getPolicyAdvisor(), new File(cliContext.getAdvisorFile())));
} else {
fetcher.setPolicyAdvisor(new TextDrivenPolicyAdvisor(fetcher.getPolicyAdvisor(), new File(cliContext.getAdvisorFile())));
}
}
}
validationEngine.getBundleValidationRules().addAll(cliContext.getBundleValidationRules());
validationEngine.setJurisdiction(CodeSystemUtilities.readCoding(cliContext.getJurisdiction()));

View File

@ -54,6 +54,8 @@ public class Params {
public static final String TERMINOLOGY = "-tx";
public static final String TERMINOLOGY_LOG = "-txLog";
public static final String TERMINOLOGY_CACHE = "-txCache";
public static final String TERMINOLOGY_ROUTING = "-tx-routing";
public static final String TERMINOLOGY_CACHE_CLEAR = "-clear-tx-cache";
public static final String LOG = "-log";
public static final String LANGUAGE = "-language";
public static final String IMPLEMENTATION_GUIDE = "-ig";
@ -86,6 +88,7 @@ public class Params {
public static final String SHOW_TIMES = "-show-times";
public static final String ALLOW_EXAMPLE_URLS = "-allow-example-urls";
public static final String OUTPUT_STYLE = "-output-style";
public static final String ADVSIOR_FILE = "-advisor-file";
public static final String DO_IMPLICIT_FHIRPATH_STRING_CONVERSION = "-implicit-fhirpath-string-conversions";
public static final String JURISDICTION = "-jurisdiction";
public static final String HTML_IN_MARKDOWN = "-html-in-markdown";
@ -339,10 +342,22 @@ public class Params {
} else {
throw new Error("Value for "+ALLOW_EXAMPLE_URLS+" not understood: "+bl);
}
} else if (args[i].equals(TERMINOLOGY_ROUTING)) {
cliContext.setShowTerminologyRouting(true);
} else if (args[i].equals(TERMINOLOGY_CACHE_CLEAR)) {
cliContext.setClearTxCache(true);
} else if (args[i].equals(SHOW_TIMES)) {
cliContext.setShowTimes(true);
} else if (args[i].equals(OUTPUT_STYLE)) {
cliContext.setOutputStyle(args[++i]);
} else if (args[i].equals(ADVSIOR_FILE)) {
cliContext.setAdvisorFile(args[++i]);
File f = new File(cliContext.getAdvisorFile());
if (!f.exists()) {
throw new Error("Cannot find advisor file "+cliContext.getAdvisorFile());
} else if (!Utilities.existsInList(Utilities.getFileExtension(f.getName()), "json", "txt")) {
throw new Error("Advisor file "+cliContext.getAdvisorFile()+" must be a .json or a .txt file");
}
} else if (args[i].equals(SCAN)) {
cliContext.setMode(EngineMode.SCAN);
} else if (args[i].equals(TERMINOLOGY)) {

View File

@ -574,7 +574,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private boolean noBindingMsgSuppressed;
private Map<String, Element> fetchCache = new HashMap<>();
private HashMap<Element, ResourceValidationTracker> resourceTracker = new HashMap<>();
private IValidationPolicyAdvisor policyAdvisor = new BasePolicyAdvisorForFullValidation(ReferenceValidationPolicy.CHECK_VALID);
long time = 0;
long start = 0;
long lastlog = 0;
@ -664,21 +663,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return this;
}
@Override
public IValidationPolicyAdvisor getPolicyAdvisor() {
return policyAdvisor;
}
@Override
public IResourceValidator setPolicyAdvisor(IValidationPolicyAdvisor advisor) {
if (advisor == null) {
throw new Error("Cannot set advisor to null");
}
this.policyAdvisor = advisor;
return this;
}
public IValidationProfileUsageTracker getTracker() {
return this.tracker;
}

View File

@ -169,5 +169,15 @@ public class BasePolicyAdvisorForFullValidation implements IValidationPolicyAdvi
return false;
}
@Override
public boolean suppressMessageId(String path, String messageId) {
return false;
}
@Override
public ReferenceValidationPolicy getReferencePolicy() {
return refpol;
}
}

View File

@ -18,60 +18,35 @@ import org.hl7.fhir.r5.utils.validation.constants.BindingKind;
import org.hl7.fhir.r5.utils.validation.constants.ContainedReferenceValidationPolicy;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.hl7.fhir.utilities.json.JsonException;
import org.hl7.fhir.utilities.json.model.JsonElement;
import org.hl7.fhir.utilities.json.model.JsonObject;
import org.hl7.fhir.utilities.json.parser.JsonParser;
import org.hl7.fhir.utilities.validation.ValidationMessage;
public class JsonDrivenPolicyAdvisor implements IValidationPolicyAdvisor {
public class JsonDrivenPolicyAdvisor extends RulesDrivenPolicyAdvisor {
private JsonObject config;
private IValidationPolicyAdvisor base;
public JsonDrivenPolicyAdvisor(IValidationPolicyAdvisor base, JsonObject config) {
this.base = base;
this.config = config;
public JsonDrivenPolicyAdvisor(IValidationPolicyAdvisor base, File source) throws JsonException, IOException {
super(base);
load(source);
}
public JsonDrivenPolicyAdvisor(String config) throws JsonException, IOException {
this.config = JsonParser.parseObject(config, true);
public JsonDrivenPolicyAdvisor(ReferenceValidationPolicy refpol, File source) throws JsonException, IOException {
super(refpol);
load(source);
}
public JsonDrivenPolicyAdvisor(File config) throws JsonException, IOException {
this.config = JsonParser.parseObject(new FileInputStream(config), true);
}
public JsonDrivenPolicyAdvisor(FileInputStream config) throws JsonException, IOException {
this.config = JsonParser.parseObject(config, true);
}
@Override
public ReferenceValidationPolicy policyForReference(IResourceValidator validator, Object appContext, String path, String url) {
return base.policyForReference(validator, appContext, path, url);
}
@Override
public ContainedReferenceValidationPolicy policyForContained(IResourceValidator validator, Object appContext, StructureDefinition structure, ElementDefinition element, String containerType, String containerId, SpecialElement containingResourceType, String path, String url) {
return base.policyForContained(validator, appContext, structure, element, containerType, containerId, containingResourceType, path, url);
}
@Override
public EnumSet<ResourceValidationAction> policyForResource(IResourceValidator validator, Object appContext, StructureDefinition type, String path) {
return base.policyForResource(validator, appContext, type, path);
}
@Override
public EnumSet<ElementValidationAction> policyForElement(IResourceValidator validator, Object appContext, StructureDefinition structure, ElementDefinition element, String path) {
return base.policyForElement(validator, appContext, structure, element, path);
}
@Override
public EnumSet<CodedContentValidationAction> policyForCodedContent(IResourceValidator validator, Object appContext, String stackPath, ElementDefinition definition, StructureDefinition structure, BindingKind kind, AdditionalBindingPurpose purpose, ValueSet valueSet, List<String> systems) {
return base.policyForCodedContent(validator, appContext, stackPath, definition, structure, kind, purpose, valueSet, systems);
}
@Override
public List<StructureDefinition> getImpliedProfilesForResource(IResourceValidator validator, Object appContext, String stackPath, ElementDefinition definition, StructureDefinition structure, Element resource, boolean valid, IMessagingServices msgServices, List<ValidationMessage> messages) {
return base.getImpliedProfilesForResource(validator, appContext, stackPath, definition, structure, resource, valid, msgServices, messages);
private void load(File source) throws JsonException, IOException {
JsonObject json = JsonParser.parseObject(source);
for (JsonElement e : json.forceArray("suppress").getItems()) {
String s = e.asString();
String id = s;
String path = null;
if (s.contains("@")) {
id = s.substring(0, s.indexOf("@"));
path = s.substring(s.indexOf("@")+1);
}
addSuppressMessageRule(id, path);
}
}
}

View File

@ -0,0 +1,176 @@
package org.hl7.fhir.validation.instance.advisor;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.utils.validation.IMessagingServices;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor.AdditionalBindingPurpose;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor.CodedContentValidationAction;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor.ElementValidationAction;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor.ResourceValidationAction;
import org.hl7.fhir.r5.utils.validation.constants.BindingKind;
import org.hl7.fhir.r5.utils.validation.constants.CodedContentValidationPolicy;
import org.hl7.fhir.r5.utils.validation.constants.ContainedReferenceValidationPolicy;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.hl7.fhir.utilities.validation.ValidationMessage;
public class RulesDrivenPolicyAdvisor extends BasePolicyAdvisorForFullValidation {
private IValidationPolicyAdvisor base;
public RulesDrivenPolicyAdvisor(ReferenceValidationPolicy refpol) {
super(refpol);
base = null;
}
public RulesDrivenPolicyAdvisor(IValidationPolicyAdvisor base) {
super(base.getReferencePolicy());
this.base = base;
}
private class SuppressMessageRule {
private String id;
private String path;
protected SuppressMessageRule(String id, String path) {
super();
this.id = id;
this.path = path;
}
public String getId() {
return id;
}
public String getPath() {
return path;
}
public boolean matches(String mid, String p) {
if (((id == null) || id.equals(mid)) && ((path == null) || path.equals(p))) {
suppressed++;
return true;
} else if (((id == null) || mid.matches(id)) && ((path == null) || p.matches(path))) {
suppressed++;
return true;
} else {
return false;
}
}
}
private List<SuppressMessageRule> suppressMessageRules = new ArrayList<>();
private int suppressed = 0;
protected void addSuppressMessageRule(String id, String path) {
suppressMessageRules.add(new SuppressMessageRule(id, path));
}
@Override
public boolean suppressMessageId(String path, String messageId) {
for (SuppressMessageRule rule : suppressMessageRules) {
if (rule.matches(messageId, path)) {
return true;
}
}
if (base != null) {
return base.suppressMessageId(path, messageId);
} else {
return super.suppressMessageId(path, messageId);
}
}
@Override
public ReferenceValidationPolicy policyForReference(IResourceValidator validator,
Object appContext,
String path,
String url) {
if (base != null) {
return base.policyForReference(validator, appContext, path, url);
} else {
return super.policyForReference(validator, appContext, path, url);
}
}
@Override
public ContainedReferenceValidationPolicy policyForContained(IResourceValidator validator,
Object appContext,
StructureDefinition structure,
ElementDefinition element,
String containerType,
String containerId,
Element.SpecialElement containingResourceType,
String path,
String url) {
if (base != null) {
return base.policyForContained(validator, appContext, structure, element, containerType, containerId, containingResourceType, path, url);
} else {
return super.policyForContained(validator, appContext, structure, element, containerType, containerId, containingResourceType, path, url);
}
}
@Override
public EnumSet<ResourceValidationAction> policyForResource(IResourceValidator validator,
Object appContext,
StructureDefinition type,
String path) {
if (base != null) {
return base.policyForResource(validator, appContext, type, path);
} else {
return super.policyForResource(validator, appContext, type, path);
}
}
@Override
public EnumSet<ElementValidationAction> policyForElement(IResourceValidator validator,
Object appContext,
StructureDefinition structure,
ElementDefinition element,
String path) {
if (base != null) {
return base.policyForElement(validator, appContext, structure, element, path);
} else {
return super.policyForElement(validator, appContext, structure, element, path);
}
}
@Override
public EnumSet<CodedContentValidationAction> policyForCodedContent(IResourceValidator validator,
Object appContext,
String stackPath,
ElementDefinition definition,
StructureDefinition structure,
BindingKind kind,
AdditionalBindingPurpose purpose,
ValueSet valueSet,
List<String> systems) {
if (base != null) {
return base.policyForCodedContent(validator, appContext, stackPath, definition, structure, kind, purpose, valueSet, systems);
} else {
return super.policyForCodedContent(validator, appContext, stackPath, definition, structure, kind, purpose, valueSet, systems);
}
}
@Override
public List<StructureDefinition> getImpliedProfilesForResource(IResourceValidator validator,
Object appContext,
String stackPath,
ElementDefinition definition,
StructureDefinition structure,
Element resource,
boolean valid,
IMessagingServices msgServices,
List<ValidationMessage> messages) {
if (base != null) {
return base.getImpliedProfilesForResource(validator, appContext, stackPath, definition, structure, resource, valid, msgServices, messages);
} else {
return super.getImpliedProfilesForResource(validator, appContext, stackPath, definition, structure, resource, valid, msgServices, messages);
}
}
}

View File

@ -0,0 +1,54 @@
package org.hl7.fhir.validation.instance.advisor;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.json.JsonException;
public class TextDrivenPolicyAdvisor extends RulesDrivenPolicyAdvisor {
public TextDrivenPolicyAdvisor(IValidationPolicyAdvisor base, File source) throws JsonException, IOException {
super(base);
load(source);
}
public TextDrivenPolicyAdvisor(ReferenceValidationPolicy refpol, File source) throws JsonException, IOException {
super(refpol);
load(source);
}
private void load(File source) throws JsonException, IOException {
BufferedReader reader = new BufferedReader(new FileReader(source));
String line = reader.readLine();
while (line != null) {
processLine(line);
line = reader.readLine();
}
reader.close();
}
private void processLine(String line) {
line = line.trim();
if (Utilities.noString(line) || line.startsWith("#")) {
return;
}
if (line.startsWith("-")) {
String s = line.substring(1).trim();
String id = s;
String path = null;
if (s.contains("@")) {
id = s.substring(0, s.indexOf("@"));
path = s.substring(s.indexOf("@")+1);
}
addSuppressMessageRule(id, path);
} else {
// ignore it for now
}
}
}

View File

@ -16,6 +16,7 @@ import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.DateType;
import org.hl7.fhir.r5.model.IntegerType;
import org.hl7.fhir.r5.model.Questionnaire;
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireAnswerConstraint;
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemAnswerOptionComponent;
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemComponent;
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemType;
@ -812,7 +813,7 @@ public class QuestionnaireValidator extends BaseValidator {
}
private boolean checkOption(List<ValidationMessage> errors, Element answer, NodeStack stack, QuestionnaireWithContext qSrc, QuestionnaireItemComponent qItem, String type) {
return checkOption(errors, answer, stack, qSrc, qItem, type, false);
return checkOption(errors, answer, stack, qSrc, qItem, type, qItem.getAnswerConstraint() == QuestionnaireAnswerConstraint.OPTIONSORSTRING);
}
private boolean checkOption(List<ValidationMessage> errors, Element answer, NodeStack stack, QuestionnaireWithContext qSrc, QuestionnaireItemComponent qItem, String type, boolean openChoice) {
@ -928,31 +929,29 @@ public class QuestionnaireValidator extends BaseValidator {
Element v = answer.getNamedChild("valueString", false);
NodeStack ns = stack.push(v, -1, null, null);
if (qItem.getAnswerOption().size() > 0) {
List<StringType> list = new ArrayList<StringType>();
boolean found = false;
boolean empty = true;
for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) {
try {
if (components.getValue() != null) {
list.add(components.getValueStringType());
}
} catch (FHIRException e) {
// If it's the wrong type, just keep going
if (components.getValue() != null && components.hasValueStringType()) {
empty = false;
found = found || components.getValue().primitiveValue().equals((v.primitiveValue()));
}
}
if (!openChoice) {
if (list.isEmpty()) {
if (empty) {
ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOOPTIONSSTRING) && ok;
} else {
boolean found = false;
for (StringType item : list) {
if (item.getValue().equals((v.primitiveValue()))) {
found = true;
break;
}
}
if (!found) {
ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOSTRING, v.primitiveValue()) && ok;
ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOSTRING, v.primitiveValue()) && ok;
}
} else {
found = false;
for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) {
if (components.getValue() != null && components.hasValueCoding()) {
Coding c = components.getValueCoding();
found = found || (c.hasDisplay() && c.getDisplay().equalsIgnoreCase(v.primitiveValue())) || (c.hasCode() && c.getCode().equalsIgnoreCase(v.primitiveValue()));
}
}
ok = warning(errors, NO_RULE_DATE, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), !found, I18nConstants.QUESTIONNAIRE_QR_ITEM_STRING_IN_CODING, v.primitiveValue()) && ok;
}
} else {
hint(errors, NO_RULE_DATE, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_STRINGNOOPTIONS);

View File

@ -34,7 +34,7 @@ Some notes on this approach:
* the messageId is directly tied to the format of the message. For some messages, there are multiple variants of the message, each with their own id, and these all need to be suppressed
* message ids are stable (including across languages), but sometimes an existing message is improved in some contexts, and new message ids are introduced in later versions
* the underlying validation still happens, even when the messages are suppressed
* for invariants in profiles, the message id is the profile URL and the invariant id e.g. http://hl7.org/fhir/StructureDefinition/DomainResource#dom-6
## Controlling what validation runs

View File

@ -920,4 +920,14 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
return null;
}
}
@Override
public boolean suppressMessageId(String path, String messageId) {
return false;
}
@Override
public ReferenceValidationPolicy getReferencePolicy() {
return ReferenceValidationPolicy.IGNORE;
}
}

View File

@ -22,7 +22,7 @@
<commons_io_version>2.17.0</commons_io_version>
<guava_version>32.0.1-jre</guava_version>
<hapi_fhir_version>6.4.1</hapi_fhir_version>
<validator_test_case_version>1.5.26</validator_test_case_version>
<validator_test_case_version>1.5.27-SNAPSHOT</validator_test_case_version>
<jackson_version>2.17.0</jackson_version>
<junit_jupiter_version>5.9.2</junit_jupiter_version>
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>