Merge branch 'master' into bugfix-condition-convert-abatement

This commit is contained in:
Grahame Grieve 2021-06-16 14:23:11 +10:00 committed by GitHub
commit ff25ab2eef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 254 additions and 45 deletions

View File

@ -1 +1,17 @@
Validator:
* Add support for $index on aggregators in FHIRPath
* don't fail with an exception if an unknown resource type appears in contained resource
* improved validation for some value sets that are based on unknown code systems
* add the -verbose parameter, and add additional verbose messages
Conversion code:
* Ignoring abatementBoolean when converting from dstu2 to r4
Other code changes:
* Fix rendering of slices so type on slicer is not hidden
* Fix rendering for most resources - remove empty tables (e.g. text element, that shouldn't render)
* Fix NPE rendering code systems with some kinds of properties
* Improve rendering of questionnaires (icons, option sets)

View File

@ -4044,6 +4044,9 @@ public class ProfileUtilities extends TranslatingUtilities {
hint = checkAdd(hint, (hasDef && element.hasSliceName() ? ": " : ""));
hint = checkAdd(hint, !hasDef ? null : gt(element.getDefinitionElement()));
}
if (element.hasSlicing()) {
sName = "Slices for "+sName;
}
Cell left = gen.new Cell(null, ref, sName, hint, null);
row.getCells().add(left);
return left;
@ -4102,9 +4105,7 @@ public class ProfileUtilities extends TranslatingUtilities {
}
} else {
res.add(genCardinality(gen, element, row, hasDef, used, null));
if (element.hasSlicing())
res.add(addCell(row, gen.new Cell(null, corePath+"profiling.html#slicing", "(Slice Definition)", null, null)));
else if (hasDef && !"0".equals(element.getMax()) && typesRow == null)
if (hasDef && !"0".equals(element.getMax()) && typesRow == null)
res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, mustSupport));
else
res.add(addCell(row, gen.new Cell()));

View File

@ -366,11 +366,13 @@ public class JsonParser extends ParserBase {
} else {
String name = rt.getAsString();
StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, context.getOverrideVersionNs()));
if (sd == null)
throw new FHIRFormatError(context.formatMessage(I18nConstants.CONTAINED_RESOURCE_DOES_NOT_APPEAR_TO_BE_A_FHIR_RESOURCE_UNKNOWN_NAME_, name));
parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, this.profileUtilities), SpecialElement.fromProperty(parent.getProperty()), elementProperty);
parent.setType(name);
parseChildren(npath, res, parent, true);
if (sd == null) {
logError(line(res), col(res), npath, IssueType.INVALID, context.formatMessage(I18nConstants.CONTAINED_RESOURCE_DOES_NOT_APPEAR_TO_BE_A_FHIR_RESOURCE_UNKNOWN_NAME_, name), IssueSeverity.FATAL);
} else {
parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, this.profileUtilities), SpecialElement.fromProperty(parent.getProperty()), elementProperty);
parent.setType(name);
parseChildren(npath, res, parent, true);
}
}
}

View File

@ -489,7 +489,7 @@ public class ExpressionNode {
if (!name.startsWith("$"))
return true;
else
return Utilities.existsInList(name, "$this", "$total");
return Utilities.existsInList(name, "$this", "$total", "$index");
}
public Kind getKind() {

View File

@ -701,18 +701,24 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
}
}
} else if (canDoTable(path, p, grandChildren, x)) {
x.addTag(getHeader()).addText(Utilities.capitalize(Utilities.camelCase(Utilities.pluralizeMe(p.getName()))));
XhtmlNode tbl = x.table("grid");
XhtmlNode xn = new XhtmlNode(NodeType.Element, getHeader());
xn.addText(Utilities.capitalize(Utilities.camelCase(Utilities.pluralizeMe(p.getName()))));
XhtmlNode tbl = new XhtmlNode(NodeType.Element, "table");
tbl.setAttribute("class", "grid");
XhtmlNode tr = tbl.tr();
tr.td().tx("-"); // work around problem with empty table rows
addColumnHeadings(tr, grandChildren);
boolean add = addColumnHeadings(tr, grandChildren);
for (BaseWrapper v : p.getValues()) {
if (v != null) {
tr = tbl.tr();
tr.td().tx("*"); // work around problem with empty table rows
addColumnValues(res, tr, grandChildren, v, showCodeDetails, displayHints, path, indent);
add = addColumnValues(res, tr, grandChildren, v, showCodeDetails, displayHints, path, indent) || add;
}
}
if (add) {
x.add(xn);
x.add(tbl);
}
} else if (isExtension(p)) {
for (BaseWrapper v : p.getValues()) {
if (v != null) {
@ -776,6 +782,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
if (x.getName().equals("p")) {
return false;
}
for (ElementDefinition e : grandChildren) {
List<PropertyWrapper> values = getValues(path, p, e);
if (values.size() > 1 || !isPrimitive(e) || !canCollapse(e))
@ -812,24 +819,32 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
return false;
}
private void addColumnHeadings(XhtmlNode tr, List<ElementDefinition> grandChildren) {
for (ElementDefinition e : grandChildren)
private boolean addColumnHeadings(XhtmlNode tr, List<ElementDefinition> grandChildren) {
boolean b = false;
for (ElementDefinition e : grandChildren) {
b = true;
tr.td().b().addText(Utilities.capitalize(tail(e.getPath())));
}
return b;
}
private void addColumnValues(ResourceWrapper res, XhtmlNode tr, List<ElementDefinition> grandChildren, BaseWrapper v, boolean showCodeDetails, Map<String, String> displayHints, String path, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome {
private boolean addColumnValues(ResourceWrapper res, XhtmlNode tr, List<ElementDefinition> grandChildren, BaseWrapper v, boolean showCodeDetails, Map<String, String> displayHints, String path, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome {
boolean b = false;
for (ElementDefinition e : grandChildren) {
PropertyWrapper p = v.getChildByName(e.getPath().substring(e.getPath().lastIndexOf(".")+1));
XhtmlNode td = tr.td();
if (p == null || p.getValues().size() == 0 || p.getValues().get(0) == null)
if (p == null || p.getValues().size() == 0 || p.getValues().get(0) == null) {
b = true;
td.tx(" ");
else {
} else {
for (BaseWrapper vv : p.getValues()) {
b = true;
td.sep(", ");
renderLeaf(res, vv, e, td, td, false, showCodeDetails, displayHints, path, indent);
}
}
}
return b;
}
private void filterGrandChildren(List<ElementDefinition> grandChildren, String string, PropertyWrapper prop) {

View File

@ -14,6 +14,7 @@ import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Expression;
import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.PrimitiveType;
import org.hl7.fhir.r5.model.Questionnaire;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
@ -60,7 +61,11 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
public boolean renderTree(XhtmlNode x, Questionnaire q) throws UnsupportedEncodingException, IOException {
boolean hasFlags = checkForFlags(q.getItem());
boolean doOpts = context.getDefinitionsTarget() == null && hasAnyOptions(q.getItem());
if (doOpts) {
x.b().tx("Structure");
}
HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context.getDestDir(), context.isInlineGraphics(), true);
TableModel model = gen.new TableModel("qtree="+q.getId(), !forResource);
model.setAlternating(true);
@ -83,9 +88,79 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
}
XhtmlNode xn = gen.generate(model, context.getLocalPrefix(), 1, null);
x.getChildNodes().add(xn);
if (doOpts) {
renderOptions(q, x);
}
return hasExt;
}
private void renderOptions(Questionnaire q, XhtmlNode x) {
if (hasAnyOptions(q.getItem())) {
x.hr();
x.para().b().tx("Option Sets");
renderOptions(q.getItem(), x);
}
}
private void renderOptions(List<QuestionnaireItemComponent> items, XhtmlNode x) {
for (QuestionnaireItemComponent i : items) {
renderItemOptions(x, i);
renderOptions(i.getItem(), x);
}
}
public void renderItemOptions(XhtmlNode x, QuestionnaireItemComponent i) {
if (i.hasAnswerOption()) {
boolean useSelect = false;
for (QuestionnaireItemAnswerOptionComponent opt : i.getAnswerOption()) {
useSelect = useSelect || opt.getInitialSelected();
}
x.an("opt-item."+i.getLinkId());
x.para().b().tx("Answer options for "+i.getLinkId());
XhtmlNode ul = x.ul();
for (QuestionnaireItemAnswerOptionComponent opt : i.getAnswerOption()) {
XhtmlNode li = ul.li();
li.style("font-size: 11px");
if (useSelect) {
if (opt.getInitialSelected()) {
li.img("icon-selected.png");
} else {
li.img("icon-not-selected.png");
}
}
if (opt.getValue().isPrimitive()) {
li.tx(opt.getValue().primitiveValue());
} else if (opt.getValue() instanceof Coding) {
Coding c = (Coding) opt.getValue();
String link = context.getWorker().getLinkForUrl(context.getSpecificationLink(), c.getSystem());
if (link == null) {
li.tx(c.getSystem()+"#"+c.getCode());
} else {
li.ah(link).tx(describeSystem(c.getSystem()));
li.tx(": "+c.getCode());
}
if (c.hasDisplay()) {
li.tx(" (\""+c.getDisplay()+"\")");
}
} else {
li.tx("??");
}
}
}
}
private boolean hasAnyOptions(List<QuestionnaireItemComponent> items) {
for (QuestionnaireItemComponent i : items) {
if (i.hasAnswerOption()) {
return true;
}
if (hasAnyOptions(i.getItem())) {
return true;
}
}
return false;
}
private boolean checkForFlags(List<QuestionnaireItemComponent> items) {
for (QuestionnaireItemComponent i : items) {
if (checkForFlags(i)) {
@ -228,7 +303,12 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
if (i.hasAnswerOption()) {
if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br"));
defn.getPieces().add(gen.new Piece(null, "Options: ", null));
defn.getPieces().add(gen.new Piece(context.getDefinitionsTarget()+"#item."+i.getLinkId(), Integer.toString(i.getAnswerOption().size())+" "+Utilities.pluralize("option", i.getAnswerOption().size()), null));
if (context.getDefinitionsTarget() == null) {
// if we don't have a definitions target, we'll add them below.
defn.getPieces().add(gen.new Piece("#opt-item."+i.getLinkId(), Integer.toString(i.getAnswerOption().size())+" "+Utilities.pluralize("option", i.getAnswerOption().size()), null));
} else {
defn.getPieces().add(gen.new Piece(context.getDefinitionsTarget()+"#item."+i.getLinkId(), Integer.toString(i.getAnswerOption().size())+" "+Utilities.pluralize("option", i.getAnswerOption().size()), null));
}
}
if (i.hasInitial()) {
for (QuestionnaireItemInitialComponent v : i.getInitial()) {
@ -725,7 +805,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
}
}
} else if (i.hasAnswerOption()) {
renderItemOptions(select, i);
}
select.option("a", "??", false);
}

View File

@ -256,7 +256,7 @@ public abstract class TerminologyRenderer extends ResourceRenderer {
if (cs == null) {
return null;
}
ConceptDefinitionComponent cc = CodeSystemUtilities.getCode(cs, code);
ConceptDefinitionComponent cc = code == null ? null : CodeSystemUtilities.getCode(cs, code);
return cc == null ? null : cc.getDisplay();
}

View File

@ -430,6 +430,9 @@ public class CodeSystemUtilities {
}
public static ConceptDefinitionComponent getCode(CodeSystem cs, String code) {
if (code == null) {
return null;
}
for (ConceptDefinitionComponent cc : cs.getConcept()) {
ConceptDefinitionComponent cd = getCode(cc, code);
if (cd != null) {

View File

@ -130,6 +130,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
ValidationResult res = null;
boolean inExpansion = false;
boolean inInclude = false;
String system = code.hasSystem() ? code.getSystem() : getValueSetSystemOrNull();
if (options.getValueSetMode() != ValueSetMode.CHECK_MEMERSHIP_ONLY) {
if (system == null && !code.hasDisplay()) { // dealing with just a plain code (enum)
@ -142,6 +143,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
code.setSystem(system);
}
inExpansion = checkExpansion(code);
inInclude = checkInclude(code);
CodeSystem cs = context.fetchCodeSystem(system);
if (cs == null) {
cs = findSpecialCodeSystem(system);
@ -181,26 +183,58 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
throw new FHIRException("No try the server");
}
} else {
// disabled waiting for discussion
throw new FHIRException("No try the server");
// inExpansion = checkExpansion(code);
inExpansion = checkExpansion(code);
inInclude = checkInclude(code);
}
// then, if we have a value set, we check it's in the value set
if (valueset != null && options.getValueSetMode() != ValueSetMode.NO_MEMBERSHIP_CHECK) {
if ((res==null || res.isOk()) && !codeInValueSet(system, code.getCode())) {
if (!inExpansion) {
res.setMessage("Not in value set "+valueset.getUrl()).setSeverity(IssueSeverity.ERROR);
} else if (warningMessage!=null) {
res = new ValidationResult(IssueSeverity.WARNING, context.formatMessage(I18nConstants.CODE_FOUND_IN_EXPANSION_HOWEVER_, warningMessage));
} else {
res.setMessage("Code found in expansion, however: " + res.getMessage());
if ((res==null || res.isOk())) {
Boolean ok = codeInValueSet(system, code.getCode());
if (ok == null || !ok) {
if (res == null) {
res = new ValidationResult(null, null);
}
if (!inExpansion && !inInclude) {
res.setMessage("Not in value set "+valueset.getUrl()).setSeverity(IssueSeverity.ERROR);
} else if (warningMessage!=null) {
res = new ValidationResult(IssueSeverity.WARNING, context.formatMessage(I18nConstants.CODE_FOUND_IN_EXPANSION_HOWEVER_, warningMessage));
} else if (inExpansion) {
res.setMessage("Code found in expansion, however: " + res.getMessage());
} else if (inInclude) {
res.setMessage("Code found in include, however: " + res.getMessage());
}
}
}
}
return res;
}
private boolean checkInclude(Coding code) {
if (valueset == null || code.getSystem() == null || code.getCode() == null) {
return false;
}
for (ConceptSetComponent inc : valueset.getCompose().getExclude()) {
if (inc.hasSystem() && inc.getSystem().equals(code.getSystem())) {
for (ConceptReferenceComponent cc : inc.getConcept()) {
if (cc.hasCode() && cc.getCode().equals(code.getCode())) {
return false;
}
}
}
}
for (ConceptSetComponent inc : valueset.getCompose().getInclude()) {
if (inc.hasSystem() && inc.getSystem().equals(code.getSystem())) {
for (ConceptReferenceComponent cc : inc.getConcept()) {
if (cc.hasCode() && cc.getCode().equals(code.getCode())) {
return true;
}
}
}
}
return false;
}
private CodeSystem findSpecialCodeSystem(String system) {
if ("urn:ietf:rfc:3986".equals(system)) {
CodeSystem cs = new CodeSystem();

View File

@ -873,6 +873,7 @@ public class FHIRPathEngine {
private Base thisItem;
private List<Base> total;
private Map<String, Base> aliases;
private int index;
public ExecutionContext(Object appInfo, Base resource, Base rootResource, Base context, Map<String, Base> aliases, Base thisItem) {
this.appInfo = appInfo;
@ -881,6 +882,7 @@ public class FHIRPathEngine {
this.rootResource = rootResource;
this.aliases = aliases;
this.thisItem = thisItem;
this.index = 0;
}
public Base getFocusResource() {
return focusResource;
@ -894,6 +896,14 @@ public class FHIRPathEngine {
public List<Base> getTotal() {
return total;
}
public void next() {
index++;
}
public Base getIndex() {
return new IntegerType(index);
}
public void addAlias(String name, List<Base> focus) throws FHIRException {
if (aliases == null) {
aliases = new HashMap<String, Base>();
@ -908,6 +918,10 @@ public class FHIRPathEngine {
public Base getAlias(String name) {
return aliases == null ? null : aliases.get(name);
}
public ExecutionContext setIndex(int i) {
index = i;
return this;
}
}
private class ExecutionTypeContext {
@ -1334,6 +1348,8 @@ public class FHIRPathEngine {
work.add(context.getThisItem());
} else if (atEntry && exp.getName().equals("$total")) {
work.addAll(context.getTotal());
} else if (atEntry && exp.getName().equals("$index")) {
work.add(context.getIndex());
} else {
for (Base item : focus) {
List<Base> outcome = execute(context, item, exp, atEntry);
@ -1439,6 +1455,8 @@ public class FHIRPathEngine {
result.update(context.getThisItem());
} else if (atEntry && exp.getName().equals("$total")) {
result.update(anything(CollectionStatus.UNORDERED));
} else if (atEntry && exp.getName().equals("$index")) {
result.addType(TypeDetails.FP_Integer);
} else if (atEntry && focus == null) {
result.update(executeContextType(context, exp.getName(), exp));
} else {
@ -4346,6 +4364,7 @@ public class FHIRPathEngine {
for (Base item : focus) {
ExecutionContext c = changeThis(context, item);
c.total = total;
c.next();
total = execute(c, pc, exp.getParameters().get(0), true);
}
return total;
@ -5117,10 +5136,12 @@ public class FHIRPathEngine {
private List<Base> funcSelect(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>();
List<Base> pc = new ArrayList<Base>();
int i = 0;
for (Base item : focus) {
pc.clear();
pc.add(item);
result.addAll(execute(changeThis(context, item), pc, exp.getParameters().get(0), true));
result.addAll(execute(changeThis(context, item).setIndex(i), pc, exp.getParameters().get(0), true));
i++;
}
return result;
}

View File

@ -65,6 +65,7 @@ public class OperationOutcomeUtilities {
if (message.getSource() != null) {
issue.getExtension().add(ToolingExtensions.makeIssueSource(message.getSource()));
}
issue.setUserData("source.msg", message);
return issue;
}

View File

@ -511,6 +511,7 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
private String locationLink;
private String txLink;
public String sliceHtml;
public String[] sliceText;
private boolean slicingHint;
private boolean signpost;
@ -771,8 +772,9 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
return sliceHtml;
}
public void setSliceHtml(String sliceHtml) {
public void setSliceHtml(String sliceHtml, String[] text) {
this.sliceHtml = sliceHtml;
this.sliceText = text;
}
public String getMessageId() {

View File

@ -262,9 +262,9 @@ public class BaseValidator {
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
//FIXME: formatMessage should be done here
protected boolean slicingHint(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, String html) {
protected boolean slicingHint(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, String html, String[] text) {
if (!thePass) {
addValidationMessage(errors, type, line, col, path, msg, IssueSeverity.INFORMATION, null).setSlicingHint(true).setSliceHtml(html);
addValidationMessage(errors, type, line, col, path, msg, IssueSeverity.INFORMATION, null).setSlicingHint(true).setSliceHtml(html, text);
}
return thePass;
}

View File

@ -97,12 +97,12 @@ public class ValidationService {
if (cliContext.getOutput() == null) {
if (r instanceof Bundle)
for (Bundle.BundleEntryComponent e : ((Bundle) r).getEntry())
ec = ec + displayOperationOutcome((OperationOutcome) e.getResource(), ((Bundle) r).getEntry().size() > 1) + ec;
ec = ec + displayOperationOutcome((OperationOutcome) e.getResource(), ((Bundle) r).getEntry().size() > 1, validator.isCrumbTrails()) + ec;
else if (r == null) {
ec = ec + 1;
System.out.println("No output from validation - nothing to validate");
} else {
ec = displayOperationOutcome((OperationOutcome) r, false);
ec = displayOperationOutcome((OperationOutcome) r, false, validator.isCrumbTrails());
}
} else {
IParser x;
@ -262,7 +262,7 @@ public class ValidationService {
return sessionId;
}
public int displayOperationOutcome(OperationOutcome oo, boolean hasMultiples) {
public int displayOperationOutcome(OperationOutcome oo, boolean hasMultiples, boolean crumbs) {
int error = 0;
int warn = 0;
int info = 0;
@ -286,6 +286,16 @@ public class ValidationService {
System.out.println((error == 0 ? "Success" : "*FAILURE*") + ": " + Integer.toString(error) + " errors, " + Integer.toString(warn) + " warnings, " + Integer.toString(info) + " notes");
for (OperationOutcome.OperationOutcomeIssueComponent issue : oo.getIssue()) {
System.out.println(getIssueSummary(issue));
if (crumbs) {
ValidationMessage vm = (ValidationMessage) issue.getUserData("source.msg");
if (vm != null) {
if (vm.sliceText != null) {
for (String s : vm.sliceText) {
System.out.println(" slice info: "+s);
}
}
}
}
}
if (hasMultiples) {
System.out.print("---");

View File

@ -56,6 +56,7 @@ public class Params {
public static final String WANT_INVARIANTS_IN_MESSAGES = "-want-invariants-in-messages";
public static final String SECURITY_CHECKS = "-security-checks";
public static final String CRUMB_TRAIL = "-crumb-trails";
public static final String VERBOSE = "-verbose";
public static final String SHOW_TIMES = "-show-times";
public static final String ALLOW_EXAMPLE_URLS = "-allow-example-urls";
@ -183,6 +184,8 @@ public class Params {
cliContext.setSecurityChecks(true);
} else if (args[i].equals(CRUMB_TRAIL)) {
cliContext.setCrumbTrails(true);
} else if (args[i].equals(VERBOSE)) {
cliContext.setCrumbTrails(true);
} else if (args[i].equals(ALLOW_EXAMPLE_URLS)) {
String bl = args[++i];
if ("true".equals(bl)) {

View File

@ -2722,7 +2722,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, areAllBaseProfiles(profiles), I18nConstants.REFERENCE_REF_CANTMATCHCHOICE, ref, asList(type.getTargetProfile()));
for (StructureDefinition sd : badProfiles.keySet()) {
slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false,
context.formatMessage(I18nConstants.DETAILS_FOR__MATCHING_AGAINST_PROFILE_, ref, sd.getUrl()), errorSummaryForSlicingAsHtml(badProfiles.get(sd)));
context.formatMessage(I18nConstants.DETAILS_FOR__MATCHING_AGAINST_PROFILE_, ref, sd.getUrl()),
errorSummaryForSlicingAsHtml(badProfiles.get(sd)), errorSummaryForSlicingAsText(badProfiles.get(sd)));
}
} else {
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() == 1, I18nConstants.REFERENCE_REF_CANTMATCHCHOICE, ref, asList(type.getTargetProfile()));
@ -2738,7 +2739,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (!isShowMessagesFromReferences()) {
warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, I18nConstants.REFERENCE_REF_MULTIPLEMATCHES, ref, asListByUrl(goodProfiles.keySet()));
for (StructureDefinition sd : badProfiles.keySet()) {
slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, context.formatMessage(I18nConstants.DETAILS_FOR__MATCHING_AGAINST_PROFILE_, ref, sd.getUrl()), errorSummaryForSlicingAsHtml(badProfiles.get(sd)));
slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, context.formatMessage(I18nConstants.DETAILS_FOR__MATCHING_AGAINST_PROFILE_, ref, sd.getUrl()),
errorSummaryForSlicingAsHtml(badProfiles.get(sd)), errorSummaryForSlicingAsText(badProfiles.get(sd)));
}
} else {
warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, I18nConstants.REFERENCE_REF_MULTIPLEMATCHES, ref, asListByUrl(goodProfiles.keySet()));
@ -2864,6 +2866,24 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return "<ul>" + b.toString() + "</ul>";
}
private String[] errorSummaryForSlicingAsText(List<ValidationMessage> list) {
List<String> res = new ArrayList<String>();
for (ValidationMessage vm : list) {
if (vm.isSlicingHint()) {
if (vm.sliceText != null) {
for (String s : vm.sliceText) {
res.add(vm.getLocation() + ": " + s);
}
} else {
res.add(vm.getLocation() + ": " + vm.getMessage());
}
} else if (vm.getLevel() == IssueSeverity.ERROR || vm.getLevel() == IssueSeverity.FATAL) {
res.add(vm.getLocation() + ": " + vm.getHtml());
}
}
return res.toArray(new String[0]);
}
private TypeRefComponent getReferenceTypeRef(List<TypeRefComponent> types) {
for (TypeRefComponent tr : types) {
if ("Reference".equals(tr.getCode())) {
@ -3619,13 +3639,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ValidatorHostContext shc = hostContext.forSlicing();
boolean pass = evaluateSlicingExpression(shc, element, path, profile, n);
if (!pass) {
slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false, (context.formatMessage(I18nConstants.DOES_NOT_MATCH_SLICE_, ed.getSliceName())), "discriminator = " + Utilities.escapeXml(n.toString()));
slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false, (context.formatMessage(I18nConstants.DOES_NOT_MATCH_SLICE_, ed.getSliceName())), "discriminator = " + Utilities.escapeXml(n.toString()), null);
for (String url : shc.getSliceRecords().keySet()) {
slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false,
context.formatMessage(I18nConstants.DETAILS_FOR__MATCHING_AGAINST_PROFILE_, stack.getLiteralPath(), url),
context.formatMessage(I18nConstants.PROFILE__DOES_NOT_MATCH_FOR__BECAUSE_OF_THE_FOLLOWING_PROFILE_ISSUES__,
url,
stack.getLiteralPath(), errorSummaryForSlicingAsHtml(shc.getSliceRecords().get(url))));
stack.getLiteralPath(), errorSummaryForSlicingAsHtml(shc.getSliceRecords().get(url))), errorSummaryForSlicingAsText(shc.getSliceRecords().get(url)));
}
}
return pass;
@ -4830,7 +4850,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
slicingHint(errors, IssueType.INFORMATIONAL, ei.line(), ei.col(), ei.getPath(), false,
context.formatMessage(I18nConstants.THIS_ELEMENT_DOES_NOT_MATCH_ANY_KNOWN_SLICE_,
profile == null ? "" : " defined in the profile " + profile.getUrl()),
context.formatMessage(I18nConstants.THIS_ELEMENT_DOES_NOT_MATCH_ANY_KNOWN_SLICE_, profile == null ? "" : I18nConstants.DEFINED_IN_THE_PROFILE + profile.getUrl()) + errorSummaryForSlicingAsHtml(ei.sliceInfo));
context.formatMessage(I18nConstants.THIS_ELEMENT_DOES_NOT_MATCH_ANY_KNOWN_SLICE_, profile == null ? "" : I18nConstants.DEFINED_IN_THE_PROFILE + profile.getUrl()) + errorSummaryForSlicingAsHtml(ei.sliceInfo),
errorSummaryForSlicingAsText(ei.sliceInfo));
} else if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.CLOSED)) {
rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_NOTSLICE, (profile == null ? "" : " defined in the profile " + profile.getUrl()), errorSummaryForSlicing(ei.sliceInfo));
}
@ -5337,7 +5358,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
public ValidationResult checkCodeOnServer(NodeStack stack, ValueSet valueset, Coding c, boolean checkMembership) {
if (checkMembership) {
return context.validateCode(new ValidationOptions(stack.getWorkingLang()), c, valueset);
return context.validateCode(new ValidationOptions(stack.getWorkingLang()).checkValueSetOnly(), c, valueset);
} else {
return context.validateCode(new ValidationOptions(stack.getWorkingLang()).noCheckValueSetMembership(), c, valueset);
}

View File

@ -19,7 +19,7 @@
<properties>
<hapi_fhir_version>5.1.0</hapi_fhir_version>
<validator_test_case_version>1.1.64</validator_test_case_version>
<validator_test_case_version>1.1.65-SNAPSHOT</validator_test_case_version>
<junit_jupiter_version>5.7.1</junit_jupiter_version>
<junit_platform_launcher_version>1.7.1</junit_platform_launcher_version>
<maven_surefire_version>3.0.0-M4</maven_surefire_version>