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 { public static class SourcedChildDefinitions {
private StructureDefinition source; private StructureDefinition source;
private List<ElementDefinition> list; private List<ElementDefinition> list;
private String path;
public SourcedChildDefinitions(StructureDefinition source, List<ElementDefinition> list) { public SourcedChildDefinitions(StructureDefinition source, List<ElementDefinition> list) {
super(); super();
this.source = source; this.source = source;
this.list = list; 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() { public StructureDefinition getSource() {
return source; return source;
} }
public List<ElementDefinition> getList() { public List<ElementDefinition> getList() {
return list; return list;
} }
public String getPath() {
return path;
}
} }
public class ElementDefinitionResolution { 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;
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceDesignationComponent; 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;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; 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; boolean first = true;
for (ResourceWrapper ext : element.extensions()) { for (ResourceWrapper ext : element.extensions()) {
if (canRender(ext)) { if (canRender(ext)) {
status.setExtensions(true);
if (first) { if (first) {
first = false; first = false;
} else { } else {
div.tx(sep); x.tx(sep);
div.tx(" "); x.tx(" ");
} }
String lbl = getExtensionLabel(ext); String lbl = getExtensionLabel(ext);
div.tx(lbl); x.tx(lbl);
div.tx(": "); x.tx(": ");
renderDataType(status, div, ext.child("value")); 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 { // public void renderExtensionsInList(XhtmlNode div, BackboneType element, String sep) throws FHIRFormatError, DefinitionException, IOException {
// boolean first = true; // boolean first = true;
@ -742,13 +764,14 @@ public class DataRenderer extends Renderer implements CodeResolver {
} }
public boolean canRenderDataType(String type) { 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"); "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 { public boolean renderDataType(RenderingStatus status, XhtmlNode x, ResourceWrapper type) throws FHIRFormatError, DefinitionException, IOException {
return renderDataType(status, null, x, type); return renderDataType(status, null, x, type);
} }
public boolean renderDataType(RenderingStatus status, XhtmlNode parent, XhtmlNode x, ResourceWrapper type) throws FHIRFormatError, DefinitionException, IOException { public boolean renderDataType(RenderingStatus status, XhtmlNode parent, XhtmlNode x, ResourceWrapper type) throws FHIRFormatError, DefinitionException, IOException {
if (type == null) { if (type == null) {
return false; return false;
@ -863,10 +886,11 @@ public class DataRenderer extends Renderer implements CodeResolver {
renderUri(status, x, type); 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")); renderQuantity(status, x, type.child("numerator"));
x.tx("/"); 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) { 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 { private void renderDateTime(RenderingStatus status, XhtmlNode x, ResourceWrapper type) throws FHIRFormatError, DefinitionException, IOException {
if (!renderPrimitiveWithNoValue(status, x, type)) { if (!renderPrimitiveWithNoValue(status, x, type)) {
x.tx(displayDateTime(type)); x.tx(displayDateTime(type));
checkRenderExtensions(status, x, type);
} }
} }
@ -1002,7 +1027,11 @@ public class DataRenderer extends Renderer implements CodeResolver {
return true; 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; 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 { 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()); 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 = ""; String s = "";
if (c.has("display")) if (c.has("display"))
s = context.getTranslated(c.child("display")); s = context.getTranslated(c.child("display"));
@ -1296,6 +1326,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
if (c.has("version")) { if (c.has("version")) {
x.tx(" "+context.formatPhrase(RenderingContext.DATA_REND_VERSION, c.primitiveValue("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) { 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); x.span(null, context.formatPhrase(RenderingContext.DATA_REND_CODES) +b.toString()).addText(s);
} }
checkRenderExtensions(status, x, cc);
} }
protected String displayIdentifier(ResourceWrapper ii) { protected String displayIdentifier(ResourceWrapper ii) {
@ -1477,7 +1509,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
return s; 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")) { if (ii.has("type")) {
ResourceWrapper type = ii.child("type"); ResourceWrapper type = ii.child("type");
if (type.has("text")) { if (type.has("text")) {
@ -1527,7 +1559,8 @@ public class DataRenderer extends Renderer implements CodeResolver {
x.tx(displayPeriod(ii.child("period"))); x.tx(displayPeriod(ii.child("period")));
} }
x.tx(")"); x.tx(")");
} }
checkRenderExtensions(status, x, ii);
} }
public static String displayHumanName(ResourceWrapper name) { 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(); StringBuilder s = new StringBuilder();
if (name.has("text")) if (name.has("text"))
s.append(context.getTranslated(name.child("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")) { if (name.has("use") && !name.primitiveValue("use").equals("usual")) {
s.append("("+context.getTranslatedCode(name.primitiveValue("use"), "http://hl7.org/fhir/name-use")+")"); 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) { private String displayAddress(ResourceWrapper address) {
@ -1604,8 +1638,9 @@ public class DataRenderer extends Renderer implements CodeResolver {
return s.toString(); return s.toString();
} }
protected void renderAddress(RenderingStatus status, XhtmlNode x, ResourceWrapper address) { protected void renderAddress(RenderingStatus status, XhtmlNode x, ResourceWrapper address) throws FHIRFormatError, DefinitionException, IOException {
x.addText(displayAddress(address)); x.addText(displayAddress(address));
checkRenderExtensions(status, x, address);
} }
@ -1788,7 +1823,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
return s.toString(); 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")) if (q.has("comparator"))
x.addText(q.primitiveValue("comparator")); x.addText(q.primitiveValue("comparator"));
if (q.has("value")) { if (q.has("value")) {
@ -1805,7 +1840,8 @@ public class DataRenderer extends Renderer implements CodeResolver {
} }
if (context.isTechnicalMode() && q.has("code")) { 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"))+"')"); 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"); st = st + "-"+rep.primitiveValue("frequencyMax");
} }
if (rep.has("period")) { 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")) if (rep.has("periodMax"))
st = st + "-"+rep.primitiveValue("periodMax"); st = st + "-"+rep.primitiveValue("periodMax");
st = st + " "+displayTimeUnits(rep.primitiveValue("periodUnit")); 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.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; 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.Resource;
import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; 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.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.ResourceWrapper; import org.hl7.fhir.r5.renderers.utils.ResourceWrapper;
import org.hl7.fhir.r5.renderers.utils.ResourceWrapper.NamedResourceWrapperList; import org.hl7.fhir.r5.renderers.utils.ResourceWrapper.NamedResourceWrapperList;
@ -49,7 +51,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
} else { } else {
ElementDefinition ed = sd.getSnapshot().getElement().get(0); ElementDefinition ed = sd.getSnapshot().getElement().get(0);
containedIds.clear(); 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) { } catch (Exception e) {
if (DEBUG) { if (DEBUG) {
@ -159,7 +161,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
return null; 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) if (ew == null)
return; return;
@ -173,7 +175,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
} else if (!renderDataType(status, parent, x, ew)) { } else if (!renderDataType(status, parent, x, ew)) {
// well, we have a cell (x) to render this thing, whatever it is // 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 // 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; boolean first = true;
x.tx(" ("); x.tx(" (");
for (ResourceWrapper child : ew.children()) { 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 { // private boolean displayLeaf(ResourceWrapper res, ResourceWrapper ew, ElementDefinition defn, XhtmlNode x, String name, boolean showCodeDetails, boolean allowLinks) throws FHIRException, UnsupportedEncodingException, IOException {
if (ew == null) // if (ew == null)
return false; // return false;
//
Map<String, String> displayHints = readDisplayHints(defn); // Map<String, String> displayHints = readDisplayHints(defn);
//
if (name.endsWith("[x]")) // if (name.endsWith("[x]"))
name = name.substring(0, name.length() - 3); // name = name.substring(0, name.length() - 3);
//
if (!showCodeDetails && ew.isPrimitive() && isDefault(displayHints, ew)) { // if (!showCodeDetails && ew.isPrimitive() && isDefault(displayHints, ew)) {
return false; // return false;
} else if (Utilities.existsInList(ew.fhirType(), "Extension")) { // } else if (Utilities.existsInList(ew.fhirType(), "Extension")) {
return false; // return false;
} else { // } else {
x.addText(name+": "+ displayDataType(ew)); // x.addText(name+": "+ displayDataType(ew));
return true; // return true;
} // }
} // }
@ -250,7 +252,8 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
return code != null && (code.equals("Element") || code.equals("BackboneElement")); 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? // do we need to do a name reference substitution?
for (ElementDefinition e : elements) { for (ElementDefinition e : elements) {
if (e.getPath().equals(path) && e.hasContentReference()) { 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) { if (results.isEmpty() && t != null && t.getType().size() == 1) {
StructureDefinition tsd = context.getWorker().fetchTypeDefinition(t.getTypeFirstRep().getWorkingCode()); 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()) { if (children.isEmpty()) {
StructureDefinition sdt = context.getWorker().fetchTypeDefinition(e.fhirType()); StructureDefinition sdt = context.getWorker().fetchTypeDefinition(e.fhirType());
if (sdt != null && (sdt.getKind() == StructureDefinitionKind.COMPLEXTYPE || sdt.getKind() == StructureDefinitionKind.PRIMITIVETYPE)) { 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 { } else {
// we don't have anything to render? // we don't have anything to render?
} }
} else { } else {
List<NamedResourceWrapperList> pl = splitExtensions(profile, e.childrenInGroups()); List<NamedResourceWrapperList> pl = splitExtensions(profile, e.childrenInGroups());
for (NamedResourceWrapperList p : pl) { 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) { 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, 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) boolean showCodeDetails, int indent, boolean round2, NamedResourceWrapperList p)
throws UnsupportedEncodingException, IOException, EOperationOutcome { throws UnsupportedEncodingException, IOException, EOperationOutcome {
if (!p.getValues().isEmpty()) { if (!p.getValues().isEmpty()) {
ElementDefinition child = getElementDefinition(children, path+"."+p.getName()); ElementDefinition child = getElementDefinition(children, path+"."+p.getName());
if (child != null) { if (child != null) {
if (!child.getBase().hasPath() || !child.getBase().getPath().startsWith("Resource.")) { 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 { boolean showCodeDetails, int indent, NamedResourceWrapperList p, ElementDefinition child, boolean round2) throws UnsupportedEncodingException, IOException, EOperationOutcome {
Map<String, String> displayHints = readDisplayHints(child); Map<String, String> displayHints = readDisplayHints(child);
if ("DomainResource.contained".equals(child.getBase().getPath())) { if ("DomainResource.contained".equals(child.getBase().getPath())) {
@ -336,8 +339,8 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
if (isExt) { if (isExt) {
status.setExtensions(true); status.setExtensions(true);
} }
List<ElementDefinition> grandChildren = getChildrenForPath(profile, allElements, path+"."+p.getName()); SourcedChildDefinitions grandChildren = getChildrenForPath(profile, path+"."+p.getName());
filterGrandChildren(grandChildren, path+"."+p.getName(), p); filterGrandChildren(grandChildren.getList(), path+"."+p.getName(), p);
if (p.getValues().size() > 0) { if (p.getValues().size() > 0) {
if (isSimple(child) && !isExt) { if (isSimple(child) && !isExt) {
XhtmlNode para = x.isPara() ? para = x : x.para(); XhtmlNode para = x.isPara() ? para = x : x.para();
@ -351,7 +354,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
if (renderAsList(child) && p.getValues().size() > 1) { if (renderAsList(child) && p.getValues().size() > 1) {
XhtmlNode list = x.ul(); XhtmlNode list = x.ul();
for (ResourceWrapper v : p.getValues()) 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 { } else {
boolean first = true; boolean first = true;
for (ResourceWrapper v : p.getValues()) { for (ResourceWrapper v : p.getValues()) {
@ -360,23 +363,23 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
} else { } else {
para.tx(", "); 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()); XhtmlNode xn = new XhtmlNode(NodeType.Element, getHeader());
xn.addText(Utilities.capitalize(Utilities.camelCase(Utilities.pluralizeMe(p.getName())))); xn.addText(Utilities.capitalize(Utilities.camelCase(Utilities.pluralizeMe(p.getName()))));
XhtmlNode tbl = new XhtmlNode(NodeType.Element, "table"); XhtmlNode tbl = new XhtmlNode(NodeType.Element, "table");
tbl.setAttribute("class", "grid"); tbl.setAttribute("class", "grid");
XhtmlNode tr = tbl.tr(); XhtmlNode tr = tbl.tr();
tr.td().style("display: none").tx("-"); // work around problem with empty table rows 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()) { for (ResourceWrapper v : p.getValues()) {
if (v != null) { if (v != null) {
tr = tbl.tr(); tr = tbl.tr();
tr.td().style("display: none").tx("*"); // work around problem with empty table rows 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) { if (add) {
@ -393,7 +396,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
XhtmlNode para = x.para(); XhtmlNode para = x.para();
para.b().addText(labelforExtension(sd, p.getUrl())); para.b().addText(labelforExtension(sd, p.getUrl()));
para.tx(": "); 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()) { } else if (!ev.isEmpty()) {
XhtmlNode bq = x.addTag("blockquote"); XhtmlNode bq = x.addTag("blockquote");
bq.para().b().addText(labelforExtension(sd, p.getUrl())); bq.para().b().addText(labelforExtension(sd, p.getUrl()));
@ -410,13 +413,13 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
XhtmlNode li = ul.li(); XhtmlNode li = ul.li();
li.tx(labelForSubExtension(vv.primitiveValue("url"), sd)); li.tx(labelForSubExtension(vv.primitiveValue("url"), sd));
li.tx(": "); 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 { } else {
for (ResourceWrapper vv : ev) { for (ResourceWrapper vv : ev) {
StructureDefinition ex = context.getWorker().fetchTypeDefinition("Extension"); StructureDefinition ex = context.getWorker().fetchTypeDefinition("Extension");
List<ElementDefinition> children = getChildrenForPath(profile, ex.getSnapshot().getElement(), "Extension"); SourcedChildDefinitions children = getChildrenForPath(ex, "Extension");
generateByProfile(status, res, ex, vv, allElements, child, children, bq, "Extension", showCodeDetails, indent+1); 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) { if (v != null) {
XhtmlNode bq = x.addTag("blockquote"); XhtmlNode bq = x.addTag("blockquote");
bq.para().b().addText(p.getName()); 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) { private String labelForSubExtension(String url, StructureDefinition sd) {
return url; return url;
@ -530,7 +556,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
return b; 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; boolean b = false;
for (ElementDefinition e : grandChildren) { for (ElementDefinition e : grandChildren) {
List<ResourceWrapper> p = v.children(e.getPath().substring(e.getPath().lastIndexOf(".")+1)); 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) { for (ResourceWrapper vv : p) {
b = true; b = true;
td.sep(", "); 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); 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) { public boolean canRender(Resource resource) {
return context.getWorker().getResourceNames().contains(resource.fhirType()); 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.EOperationOutcome;
import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.r5.utils.XVerExtensionManager;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece;
@ -369,8 +368,10 @@ public abstract class ResourceRenderer extends DataRenderer {
} else { } else {
x.tx("??"); x.tx("??");
} }
checkRenderExtensions(status, x, type);
} }
public void renderReference(ResourceWrapper res, HierarchicalTableGenerator gen, List<Piece> pieces, Reference r, boolean allowLinks) throws UnsupportedEncodingException, IOException { public void renderReference(ResourceWrapper res, HierarchicalTableGenerator gen, List<Piece> pieces, Reference r, boolean allowLinks) throws UnsupportedEncodingException, IOException {
if (r == null) { if (r == null) {
pieces.add(gen.new Piece(null, "null!", 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; ITerminologyClient makeClient(String id, String url, String userAgent, ToolingClientLogger logger) throws URISyntaxException;
String getVersion(); 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--"; 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 List<TerminologyClientContext> serverList = new ArrayList<>(); // clients by server address
private Map<String, TerminologyClientContext> serverMap = new HashMap<>(); // 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 Map<String, ServerOptionList> resMap = new HashMap<>(); // client resolution list
private List<String> internalLog = new ArrayList<>(); private List<InternalLogEvent> internalLog = new ArrayList<>();
protected Parameters expParameters; protected Parameters expParameters;
private TerminologyCache cache; private TerminologyCache cache;
@ -124,6 +160,7 @@ public class TerminologyClientManager {
monitorServiceURL = other.monitorServiceURL; monitorServiceURL = other.monitorServiceURL;
factory = other.factory; factory = other.factory;
usage = other.usage; usage = other.usage;
internalLog = other.internalLog;
} }
@ -150,6 +187,7 @@ public class TerminologyClientManager {
} }
} }
if (ok) { if (ok) {
log(vs, s, systems, choices, "Found authoritative server "+s);
return findClient(s, systems, expand); return findClient(s, systems, expand);
} }
} }
@ -165,6 +203,7 @@ public class TerminologyClientManager {
} }
} }
if (ok) { if (ok) {
log(vs, s, systems, choices, "Found partially authoritative server "+s);
return findClient(s, systems, expand); return findClient(s, systems, expand);
} }
} }
@ -180,6 +219,7 @@ public class TerminologyClientManager {
} }
} }
if (ok) { if (ok) {
log(vs, s, systems, choices, "Found candidate server "+s);
return findClient(s, systems, expand); 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", "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://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/")) { "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); return serverList.get(0);
} }
} }
@ -206,29 +247,37 @@ public class TerminologyClientManager {
el = getMaster().getAddress(); el = getMaster().getAddress();
} }
if (systems.size() == 1) { 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 { } 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); return findClient(el, systems, expand);
} else { } else {
if (systems.size() == 1) { 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 { } 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); return findClient(serverList.get(0).getAddress(), systems, expand);
} }
} else { } else {
if (systems.size() == 1) { 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 { } 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); 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) { private TerminologyClientContext findClient(String server, Set<String> systems, boolean expand) {
TerminologyClientContext client = serverMap.get(server); TerminologyClientContext client = serverMap.get(server);
if (client == null) { if (client == null) {
@ -297,8 +346,8 @@ public class TerminologyClientManager {
return ret; return ret;
} catch (Exception e) { } catch (Exception e) {
String msg = "Error resolving system "+url+": "+e.getMessage()+" ("+request+")"; String msg = "Error resolving system "+url+": "+e.getMessage()+" ("+request+")";
if (!internalLog.contains(msg)) { if (!hasMessage(msg)) {
internalLog.add(msg); internalLog.add(new InternalLogEvent(msg));
} }
if (!monitorServiceURL.contains("tx.fhir.org")) { if (!monitorServiceURL.contains("tx.fhir.org")) {
e.printStackTrace(); 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() { public List<TerminologyClientContext> serverList() {
return serverList; return serverList;
} }
@ -410,10 +468,6 @@ public class TerminologyClientManager {
} }
} }
public List<String> getInternalLog() {
return internalLog;
}
public List<TerminologyClientContext> getServerList() { public List<TerminologyClientContext> getServerList() {
return serverList; return serverList;
} }
@ -518,8 +572,8 @@ public class TerminologyClientManager {
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
String msg = "Error resolving valueSet "+canonical+": "+e.getMessage()+" ("+request+")"; String msg = "Error resolving valueSet "+canonical+": "+e.getMessage()+" ("+request+")";
if (!internalLog.contains(msg)) { if (!hasMessage(msg)) {
internalLog.add(msg); internalLog.add(new InternalLogEvent(msg));
} }
e.printStackTrace(); e.printStackTrace();
return null; return null;
@ -598,8 +652,8 @@ public class TerminologyClientManager {
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
String msg = "Error resolving valueSet "+canonical+": "+e.getMessage()+" ("+request+")"; String msg = "Error resolving valueSet "+canonical+": "+e.getMessage()+" ("+request+")";
if (!internalLog.contains(msg)) { if (!hasMessage(msg)) {
internalLog.add(msg); internalLog.add(new InternalLogEvent(msg));
} }
e.printStackTrace(); e.printStackTrace();
return null; return null;
@ -614,5 +668,8 @@ public class TerminologyClientManager {
} }
return false; return false;
} }
public List<InternalLogEvent> getInternalLog() {
return internalLog;
}
} }

View File

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

View File

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

View File

@ -16,6 +16,21 @@ import org.hl7.fhir.r5.utils.validation.constants.BindingKind;
public interface IValidationPolicyAdvisor { 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 * @param validator
@ -169,5 +184,6 @@ public interface IValidationPolicyAdvisor {
boolean valid, boolean valid,
IMessagingServices msgServices, IMessagingServices msgServices,
List<ValidationMessage> messages); 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_ONLYONEI = "Questionnaire_QR_Item_OnlyOneI";
public static final String QUESTIONNAIRE_QR_ITEM_ORDER = "Questionnaire_QR_Item_Order"; 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_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_TEXT = "Questionnaire_QR_Item_Text";
public static final String QUESTIONNAIRE_QR_ITEM_TIMENOOPTIONS = "Questionnaire_QR_Item_TimeNoOptions"; public static final String QUESTIONNAIRE_QR_ITEM_TIMENOOPTIONS = "Questionnaire_QR_Item_TimeNoOptions";
public static final String QUESTIONNAIRE_QR_ITEM_WRONGTYPE = "Questionnaire_QR_Item_WrongType"; 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_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_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_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_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_NoString = The string {0} is not a valid option
Questionnaire_QR_Item_NoTime = The time {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.XVerExtensionManager.XVerExtensionStatus;
import org.hl7.fhir.r5.utils.validation.IMessagingServices; import org.hl7.fhir.r5.utils.validation.IMessagingServices;
import org.hl7.fhir.r5.utils.validation.IResourceValidator; 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.IValidatorResourceFetcher;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier.IValidationContextResourceLoader; 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.BestPracticeWarningLevel;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.FhirPublication; import org.hl7.fhir.utilities.FhirPublication;
import org.hl7.fhir.utilities.StandardsStatus; 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.validation.ValidationMessage.Source;
import org.hl7.fhir.utilities.xhtml.XhtmlNode; import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.hl7.fhir.validation.cli.utils.ValidationLevel; 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.IndexedElement;
import org.hl7.fhir.validation.instance.utils.NodeStack; 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 BUNDLE = "Bundle";
protected final String LAST_UPDATED = "lastUpdated"; protected final String LAST_UPDATED = "lastUpdated";
protected String sessionId;
protected BaseValidator parent; protected BaseValidator parent;
protected Source source;
protected IWorkerContext context; protected IWorkerContext context;
protected ValidationTimeTracker timeTracker = new ValidationTimeTracker(); protected ValidationTimeTracker timeTracker = new ValidationTimeTracker();
protected XVerExtensionManager xverManager; 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 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) { public BaseValidator(IWorkerContext context, XVerExtensionManager xverManager, boolean debug) {
super(); super();
@ -193,6 +202,8 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
this.xverManager = new XVerExtensionManager(context); this.xverManager = new XVerExtensionManager(context);
} }
this.debug = debug; this.debug = debug;
sessionId = Utilities.makeUuidLC();
policyAdvisor = new BasePolicyAdvisorForFullValidation(ReferenceValidationPolicy.CHECK_VALID);
urlRegex = Constants.URI_REGEX_XVER.replace("$$", CommaSeparatedStringBuilder.join("|", context.getResourceNames())); 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.usageContexts.addAll(parent.usageContexts);
this.baseOptions = parent.baseOptions; this.baseOptions = parent.baseOptions;
this.fetcher = parent.fetcher; this.fetcher = parent.fetcher;
this.sessionId = parent.sessionId;
this.policyAdvisor = parent.policyAdvisor;
} }
private boolean doingLevel(IssueSeverity error) { private boolean doingLevel(IssueSeverity error) {
@ -261,75 +274,22 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
protected String urlRegex; protected String urlRegex;
/** private boolean suppressMsg(String path, String theMessage) {
* Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails if (policyAdvisor == null) {
* return false;
* @param thePass } else {
* Set this parameter to <code>false</code> if the validation does not pass return policyAdvisor.suppressMessageId(path, theMessage);
* @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);
} }
return thePass;
} }
protected boolean fail(List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) { 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); String msg = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, line, col, path, msg, IssueSeverity.FATAL, theMessage); addValidationMessage(errors, ruleDate, type, line, col, path, msg, IssueSeverity.FATAL, theMessage);
} }
return thePass; 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 //TODO: i18n
protected boolean grammarWord(String w) { 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"); 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) * @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) { 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); String message = context.formatMessage(msg);
addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.INFORMATION, 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) * @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) { 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); String message = context.formatMessage(msg);
addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.INFORMATION, msg).setInvId(invId); 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) * @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) { 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); String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.INFORMATION, theMessage); 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) { 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); String message = context.formatMessagePlural(num, theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.INFORMATION, theMessage); 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) { 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); String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.INFORMATION, Source.TerminologyEngine, theMessage).setTxLink(txLink); 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) * @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) { 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 path = toPath(pathParts);
String message = context.formatMessage(theMessage, theMessageArguments); String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, -1, -1, path, message, IssueSeverity.INFORMATION, theMessage); 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) * @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) { 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); String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, -1, -1, path, message, IssueSeverity.INFORMATION, null); 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) * @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) { 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); String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.ERROR, theMessage); 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) { 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); String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.ERROR, invId).setInvId(invId); 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) { 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); String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, stack.line(), stack.col(), stack.getLiteralPath(), message, IssueSeverity.ERROR, theMessage); 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) { 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); String message = context.formatMessagePlural(num, theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.ERROR, theMessage); 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) { 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); String message = context.formatMessage(theMessage, theMessageArguments);
ValidationMessage vm = new ValidationMessage(Source.TerminologyEngine, type, line, col, path, message, IssueSeverity.ERROR).setMessageId(idForMessage(theMessage, message)); ValidationMessage vm = new ValidationMessage(Source.TerminologyEngine, type, line, col, path, message, IssueSeverity.ERROR).setMessageId(idForMessage(theMessage, message));
vm.setRuleDate(ruleDate); 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) * @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) { 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 path = toPath(pathParts);
String message = context.formatMessage(theMessage, theMessageArguments); String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, -1, -1, path, message, IssueSeverity.ERROR, theMessage); 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) { 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); String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, -1, -1, path, message, IssueSeverity.ERROR, theMessage); 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) { 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); String message = context.formatMessagePlural(num, theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, -1, -1, path, message, IssueSeverity.ERROR, theMessage); 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) * @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) { 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); String nmsg = context.formatMessage(msg, theMessageArguments);
IssueSeverity severity = IssueSeverity.WARNING; IssueSeverity severity = IssueSeverity.WARNING;
addValidationMessage(errors, ruleDate, type, line, col, path, nmsg, severity, msg); 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) { 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); String nmsg = context.formatMessage(msg, theMessageArguments);
IssueSeverity severity = IssueSeverity.WARNING; IssueSeverity severity = IssueSeverity.WARNING;
addValidationMessage(errors, ruleDate, type, line, col, path, nmsg, severity, id); 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) { 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); String nmsg = context.formatMessage(msg, theMessageArguments);
IssueSeverity severity = IssueSeverity.WARNING; IssueSeverity severity = IssueSeverity.WARNING;
String id = idForMessage(msg, nmsg); 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) { 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); String nmsg = context.formatMessagePlural(num, msg, theMessageArguments);
IssueSeverity severity = IssueSeverity.WARNING; IssueSeverity severity = IssueSeverity.WARNING;
addValidationMessage(errors, ruleDate, type, line, col, path, nmsg, severity, msg); 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) * @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) { 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); 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)); ValidationMessage vmsg = new ValidationMessage(Source.TerminologyEngine, type, line, col, path, nmsg, IssueSeverity.WARNING).setTxLink(txLink).setMessageId(idForMessage(msg, nmsg));
vmsg.setRuleDate(ruleDate); vmsg.setRuleDate(ruleDate);
@ -744,7 +704,9 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
vmsg.setMessageId(issue.getExtensionString(ToolingExtensions.EXT_ISSUE_MSG_ID)); vmsg.setMessageId(issue.getExtensionString(ToolingExtensions.EXT_ISSUE_MSG_ID));
} }
errors.add(vmsg); if (!suppressMsg(path, vmsg.getMessageId())) {
errors.add(vmsg);
}
return 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) * @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) { 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); String nmsg = context.formatMessage(msg, theMessageArguments);
ValidationMessage vmsg = new ValidationMessage(Source.TerminologyEngine, type, line, col, path, nmsg, IssueSeverity.WARNING).setTxLink(txLink).setMessageId(msg); ValidationMessage vmsg = new ValidationMessage(Source.TerminologyEngine, type, line, col, path, nmsg, IssueSeverity.WARNING).setTxLink(txLink).setMessageId(msg);
vmsg.setRuleDate(ruleDate); 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) { 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); String nmsg = context.formatMessage(msg, theMessageArguments);
IssueSeverity lvl = isError ? IssueSeverity.ERROR : IssueSeverity.WARNING; IssueSeverity lvl = isError ? IssueSeverity.ERROR : IssueSeverity.WARNING;
if (doingLevel(lvl)) { 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) { 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); String nmsg = context.formatMessage(msg, theMessageArguments);
IssueSeverity lvl = isError ? IssueSeverity.ERROR : IssueSeverity.INFORMATION; IssueSeverity lvl = isError ? IssueSeverity.ERROR : IssueSeverity.INFORMATION;
if (doingLevel(lvl)) { 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) * @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) { 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 path = toPath(pathParts);
String message = context.formatMessage(theMessage, theMessageArguments); String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, -1, -1, path, message, IssueSeverity.WARNING, theMessage); 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) * @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) { 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); String message = context.formatMessage(msg, theMessageArguments);
addValidationMessage(errors, ruleDate, type, -1, -1, path, message, IssueSeverity.WARNING, null); 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) * @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) { 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); String message = context.formatMessage(msg, theMessageArguments);
IssueSeverity lvl = warning ? IssueSeverity.WARNING : IssueSeverity.INFORMATION; IssueSeverity lvl = warning ? IssueSeverity.WARNING : IssueSeverity.INFORMATION;
if (doingLevel(lvl)) { 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) * @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) { 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); addValidationMessage(errors, ruleDate, type, path, msg, html, IssueSeverity.WARNING, null);
} }
return thePass; 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) * @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) { 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); String nmsg = context.formatMessage(msg, theMessageArguments);
addValidationMessage(errors, ruleDate, type, path, nmsg, html, IssueSeverity.WARNING, msg); 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) * @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) { 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); String nmsg = context.formatMessage(msg, theMessageArguments);
addValidationMessage(errors, ruleDate, type, line, col, path, nmsg, IssueSeverity.INFORMATION, msg); 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) * @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) { 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 path = toPath(pathParts);
String message = context.formatMessage(theMessage, theMessageArguments); String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, ruleDate, type, -1, -1, path, message, IssueSeverity.INFORMATION, theMessage); 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) * @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) { 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); String nmsg = context.formatMessage(msg, theMessageArguments);
addValidationMessage(errors, ruleDate, type, path, nmsg, html, IssueSeverity.INFORMATION, msg); addValidationMessage(errors, ruleDate, type, path, nmsg, html, IssueSeverity.INFORMATION, msg);
} }
@ -1681,4 +1643,17 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
return false; 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); 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); ((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())); System.out.println("Done. " + tt.report()+". Max Memory = "+Utilities.describeSize(Runtime.getRuntime().maxMemory()));
SystemExitManager.finish(); SystemExitManager.finish();
} }

View File

@ -132,6 +132,12 @@ public class CliContext {
@JsonProperty("showTimes") @JsonProperty("showTimes")
private boolean showTimes = false; private boolean showTimes = false;
@JsonProperty("showTerminologyRouting")
private boolean showTerminologyRouting = false;
@JsonProperty("clearTxCache")
private boolean clearTxCache = false;
@JsonProperty("locale") @JsonProperty("locale")
private String locale = Locale.ENGLISH.toLanguageTag(); private String locale = Locale.ENGLISH.toLanguageTag();
@ -172,6 +178,9 @@ public class CliContext {
@JsonProperty("noExperimentalContent") @JsonProperty("noExperimentalContent")
private boolean noExperimentalContent; private boolean noExperimentalContent;
@JsonProperty("advisorFile")
private String advisorFile;
@JsonProperty("baseEngine") @JsonProperty("baseEngine")
public String getBaseEngine() { public String getBaseEngine() {
return baseEngine; return baseEngine;
@ -754,6 +763,22 @@ public class CliContext {
this.showTimes = showTimes; 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() { public String getOutputStyle() {
return outputStyle; return outputStyle;
} }
@ -852,6 +877,7 @@ public class CliContext {
Objects.equals(watchScanDelay, that.watchScanDelay) && Objects.equals(watchScanDelay, that.watchScanDelay) &&
Objects.equals(unknownCodeSystemsCauseErrors, that.unknownCodeSystemsCauseErrors) && Objects.equals(unknownCodeSystemsCauseErrors, that.unknownCodeSystemsCauseErrors) &&
Objects.equals(noExperimentalContent, that.noExperimentalContent) && Objects.equals(noExperimentalContent, that.noExperimentalContent) &&
Objects.equals(advisorFile, that.advisorFile) &&
Objects.equals(watchSettleTime, that.watchSettleTime) ; Objects.equals(watchSettleTime, that.watchSettleTime) ;
} }
@ -860,7 +886,7 @@ public class CliContext {
return Objects.hash(baseEngine, doNative, extensions, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching, 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, 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, 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 @Override
@ -922,6 +948,7 @@ public class CliContext {
", watchScanDelay=" + watchScanDelay + ", watchScanDelay=" + watchScanDelay +
", unknownCodeSystemsCauseErrors=" + unknownCodeSystemsCauseErrors + ", unknownCodeSystemsCauseErrors=" + unknownCodeSystemsCauseErrors +
", noExperimentalContent=" + noExperimentalContent + ", noExperimentalContent=" + noExperimentalContent +
", advisorFile=" + advisorFile +
'}'; '}';
} }
@ -1002,5 +1029,15 @@ public class CliContext {
this.noExperimentalContent = noExperimentalContent; 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.IWorkerContext;
import org.hl7.fhir.r5.context.IWorkerContextManager; import org.hl7.fhir.r5.context.IWorkerContextManager;
import org.hl7.fhir.r5.elementmodel.Element; 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.CanonicalResource;
import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.client.ITerminologyClient; 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.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor; import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher; 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.json.parser.JsonParser;
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
import org.hl7.fhir.utilities.npm.NpmPackage; 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.cli.utils.Common;
import org.hl7.fhir.validation.instance.advisor.BasePolicyAdvisorForFullValidation; import org.hl7.fhir.validation.instance.advisor.BasePolicyAdvisorForFullValidation;
import javax.annotation.Nonnull; 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<>(); List<String> mappingsUris = new ArrayList<>();
private FilesystemPackageCacheManager pcm; private FilesystemPackageCacheManager pcm;
@ -56,12 +59,13 @@ public class StandAloneValidatorFetcher extends BasePolicyAdvisorForFullValidati
private Map<String, Boolean> urlList = new HashMap<>(); private Map<String, Boolean> urlList = new HashMap<>();
private Map<String, String> pidList = new HashMap<>(); private Map<String, String> pidList = new HashMap<>();
private Map<String, NpmPackage> pidMap = new HashMap<>(); private Map<String, NpmPackage> pidMap = new HashMap<>();
private IValidationPolicyAdvisor policyAdvisor;
public StandAloneValidatorFetcher(FilesystemPackageCacheManager pcm, IWorkerContext context, IPackageInstaller installer) { public StandAloneValidatorFetcher(FilesystemPackageCacheManager pcm, IWorkerContext context, IPackageInstaller installer) {
super(ReferenceValidationPolicy.IGNORE);
this.pcm = pcm; this.pcm = pcm;
this.context = context; this.context = context;
this.installer = installer; this.installer = installer;
this.policyAdvisor = new BasePolicyAdvisorForFullValidation(ReferenceValidationPolicy.IGNORE);
} }
@Override @Override
@ -297,4 +301,55 @@ public class StandAloneValidatorFetcher extends BasePolicyAdvisorForFullValidati
return new HashSet<>(); 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.StructureDefinitionSpreadsheetGenerator;
import org.hl7.fhir.r5.renderers.spreadsheets.ValueSetSpreadsheetGenerator; import org.hl7.fhir.r5.renderers.spreadsheets.ValueSetSpreadsheetGenerator;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; 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.r5.terminologies.utilities.TerminologyCache;
import org.hl7.fhir.utilities.FhirPublication; import org.hl7.fhir.utilities.FhirPublication;
import org.hl7.fhir.utilities.SystemExitManager; 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.Common;
import org.hl7.fhir.validation.cli.utils.EngineMode; import org.hl7.fhir.validation.cli.utils.EngineMode;
import org.hl7.fhir.validation.cli.utils.VersionSourceInformation; 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 { public class ValidationService {
@ -155,6 +158,7 @@ public class ValidationService {
if (request.getCliContext().isShowTimes()) { if (request.getCliContext().isShowTimes()) {
response.getValidationTimes().put(fileToValidate.getFileName(), validatedFragments.getValidationTime()); response.getValidationTimes().put(fileToValidate.getFileName(), validatedFragments.getValidationTime());
} }
} }
} }
@ -289,6 +293,19 @@ public class ValidationService {
TextFile.stringToFile(html, cliContext.getHtmlOutput()); TextFile.stringToFile(html, cliContext.getHtmlOutput());
System.out.println("HTML Summary in " + 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 (watch != ValidatorWatchMode.NONE) {
if (statusNeeded) { if (statusNeeded) {
@ -545,6 +562,10 @@ public class ValidationService {
System.out.println(" No Terminology Cache"); System.out.println(" No Terminology Cache");
} else { } else {
System.out.println(" Terminology Cache at "+validationEngine.getContext().getTxCache().getFolder()); 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... "); System.out.print(" Get set... ");
validationEngine.setQuestionnaireMode(cliContext.getQuestionnaireMode()); validationEngine.setQuestionnaireMode(cliContext.getQuestionnaireMode());
@ -584,6 +605,13 @@ public class ValidationService {
validationEngine.setFetcher(fetcher); validationEngine.setFetcher(fetcher);
validationEngine.getContext().setLocator(fetcher); validationEngine.getContext().setLocator(fetcher);
validationEngine.setPolicyAdvisor(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.getBundleValidationRules().addAll(cliContext.getBundleValidationRules());
validationEngine.setJurisdiction(CodeSystemUtilities.readCoding(cliContext.getJurisdiction())); 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 = "-tx";
public static final String TERMINOLOGY_LOG = "-txLog"; public static final String TERMINOLOGY_LOG = "-txLog";
public static final String TERMINOLOGY_CACHE = "-txCache"; 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 LOG = "-log";
public static final String LANGUAGE = "-language"; public static final String LANGUAGE = "-language";
public static final String IMPLEMENTATION_GUIDE = "-ig"; 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 SHOW_TIMES = "-show-times";
public static final String ALLOW_EXAMPLE_URLS = "-allow-example-urls"; public static final String ALLOW_EXAMPLE_URLS = "-allow-example-urls";
public static final String OUTPUT_STYLE = "-output-style"; 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 DO_IMPLICIT_FHIRPATH_STRING_CONVERSION = "-implicit-fhirpath-string-conversions";
public static final String JURISDICTION = "-jurisdiction"; public static final String JURISDICTION = "-jurisdiction";
public static final String HTML_IN_MARKDOWN = "-html-in-markdown"; public static final String HTML_IN_MARKDOWN = "-html-in-markdown";
@ -339,10 +342,22 @@ public class Params {
} else { } else {
throw new Error("Value for "+ALLOW_EXAMPLE_URLS+" not understood: "+bl); 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)) { } else if (args[i].equals(SHOW_TIMES)) {
cliContext.setShowTimes(true); cliContext.setShowTimes(true);
} else if (args[i].equals(OUTPUT_STYLE)) { } else if (args[i].equals(OUTPUT_STYLE)) {
cliContext.setOutputStyle(args[++i]); 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)) { } else if (args[i].equals(SCAN)) {
cliContext.setMode(EngineMode.SCAN); cliContext.setMode(EngineMode.SCAN);
} else if (args[i].equals(TERMINOLOGY)) { } else if (args[i].equals(TERMINOLOGY)) {

View File

@ -574,7 +574,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private boolean noBindingMsgSuppressed; private boolean noBindingMsgSuppressed;
private Map<String, Element> fetchCache = new HashMap<>(); private Map<String, Element> fetchCache = new HashMap<>();
private HashMap<Element, ResourceValidationTracker> resourceTracker = new HashMap<>(); private HashMap<Element, ResourceValidationTracker> resourceTracker = new HashMap<>();
private IValidationPolicyAdvisor policyAdvisor = new BasePolicyAdvisorForFullValidation(ReferenceValidationPolicy.CHECK_VALID);
long time = 0; long time = 0;
long start = 0; long start = 0;
long lastlog = 0; long lastlog = 0;
@ -664,21 +663,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return this; 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() { public IValidationProfileUsageTracker getTracker() {
return this.tracker; return this.tracker;
} }

View File

@ -169,5 +169,15 @@ public class BasePolicyAdvisorForFullValidation implements IValidationPolicyAdvi
return false; 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.ContainedReferenceValidationPolicy;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy; import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.hl7.fhir.utilities.json.JsonException; 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.model.JsonObject;
import org.hl7.fhir.utilities.json.parser.JsonParser; import org.hl7.fhir.utilities.json.parser.JsonParser;
import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage;
public class JsonDrivenPolicyAdvisor implements IValidationPolicyAdvisor { public class JsonDrivenPolicyAdvisor extends RulesDrivenPolicyAdvisor {
private JsonObject config; public JsonDrivenPolicyAdvisor(IValidationPolicyAdvisor base, File source) throws JsonException, IOException {
private IValidationPolicyAdvisor base; super(base);
load(source);
public JsonDrivenPolicyAdvisor(IValidationPolicyAdvisor base, JsonObject config) {
this.base = base;
this.config = config;
} }
public JsonDrivenPolicyAdvisor(String config) throws JsonException, IOException { public JsonDrivenPolicyAdvisor(ReferenceValidationPolicy refpol, File source) throws JsonException, IOException {
this.config = JsonParser.parseObject(config, true); super(refpol);
load(source);
} }
public JsonDrivenPolicyAdvisor(File config) throws JsonException, IOException { private void load(File source) throws JsonException, IOException {
this.config = JsonParser.parseObject(new FileInputStream(config), true); JsonObject json = JsonParser.parseObject(source);
} for (JsonElement e : json.forceArray("suppress").getItems()) {
String s = e.asString();
public JsonDrivenPolicyAdvisor(FileInputStream config) throws JsonException, IOException { String id = s;
this.config = JsonParser.parseObject(config, true); String path = null;
} if (s.contains("@")) {
id = s.substring(0, s.indexOf("@"));
@Override path = s.substring(s.indexOf("@")+1);
public ReferenceValidationPolicy policyForReference(IResourceValidator validator, Object appContext, String path, String url) { }
return base.policyForReference(validator, appContext, path, url); addSuppressMessageRule(id, path);
} }
@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);
} }
} }

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.DateType;
import org.hl7.fhir.r5.model.IntegerType; import org.hl7.fhir.r5.model.IntegerType;
import org.hl7.fhir.r5.model.Questionnaire; 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.QuestionnaireItemAnswerOptionComponent;
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemComponent; import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemComponent;
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemType; 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) { 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) { 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); Element v = answer.getNamedChild("valueString", false);
NodeStack ns = stack.push(v, -1, null, null); NodeStack ns = stack.push(v, -1, null, null);
if (qItem.getAnswerOption().size() > 0) { if (qItem.getAnswerOption().size() > 0) {
List<StringType> list = new ArrayList<StringType>(); boolean found = false;
boolean empty = true;
for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) {
try { if (components.getValue() != null && components.hasValueStringType()) {
if (components.getValue() != null) { empty = false;
list.add(components.getValueStringType()); found = found || components.getValue().primitiveValue().equals((v.primitiveValue()));
}
} catch (FHIRException e) {
// If it's the wrong type, just keep going
} }
} }
if (!openChoice) { 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; ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOOPTIONSSTRING) && ok;
} else { } else {
boolean found = false; ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOSTRING, v.primitiveValue()) && ok;
for (StringType item : list) { }
if (item.getValue().equals((v.primitiveValue()))) { } else {
found = true; found = false;
break; for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) {
} if (components.getValue() != null && components.hasValueCoding()) {
} Coding c = components.getValueCoding();
if (!found) { found = found || (c.hasDisplay() && c.getDisplay().equalsIgnoreCase(v.primitiveValue())) || (c.hasCode() && c.getCode().equalsIgnoreCase(v.primitiveValue()));
ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOSTRING, v.primitiveValue()) && ok;
} }
} }
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 { } else {
hint(errors, NO_RULE_DATE, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_STRINGNOOPTIONS); 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 * 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 * 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 * 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 ## Controlling what validation runs

View File

@ -920,4 +920,14 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
return null; 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> <commons_io_version>2.17.0</commons_io_version>
<guava_version>32.0.1-jre</guava_version> <guava_version>32.0.1-jre</guava_version>
<hapi_fhir_version>6.4.1</hapi_fhir_version> <hapi_fhir_version>6.4.1</hapi_fhir_version>
<validator_test_case_version>1.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> <jackson_version>2.17.0</jackson_version>
<junit_jupiter_version>5.9.2</junit_jupiter_version> <junit_jupiter_version>5.9.2</junit_jupiter_version>
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version> <junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>