* fix issue validating # references

* Mark it has an error if a JSON Array is empty

* Don't make wrong error reports for profiling resources in bundles

* * Render binding description in profile tables if it doesn't contain paragraphs
* fix bug with wrong value for contentReference in derived profiles (profiles do not and cannot change the value)

* fix bug with wrong value for contentReference in derived profiles (profiles do not and cannot change the value) (missed testing change)

* * fix bug not recognising some content as xml or json

* improved markdown support in table generator

* * fix bug checking unfixed values for HumanName patterns
* fix bug checking patterns (missed in some circumstances)
* fix bug checking type of resources in bundles
* improve messages around cardinality errors in profiles

* add parameter -html-output for enhanced presentation of slicing information (issue #283)
This commit is contained in:
Grahame Grieve 2020-10-30 00:17:24 +11:00 committed by GitHub
parent e1ecd06282
commit c5255a0f80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 541 additions and 70 deletions

View File

@ -0,0 +1,12 @@
Validator Changes:
* Mark it has an error if a JSON Array is empty
* Don't make wrong error reports for profiling resources in bundles
* fix bug checking unfixed values for HumanName patterns
* fix bug checking patterns (missed in some circumstances)
* fix bug checking type of resources in bundles
* improve messages around cardinality errors in profiles
* add parameter -html-output for enhanced presentation of slicing information
Other code changes:
* Render binding description in profile tables if it doesn't contain paragraphs
* fix bug with wrong value for contentReference in derived profiles (profiles do not and cannot change the value)

View File

@ -111,6 +111,7 @@ import org.hl7.fhir.r5.utils.XVerExtensionManager.XVerExtensionStatus;
import org.hl7.fhir.r5.utils.formats.CSVWriter; import org.hl7.fhir.r5.utils.formats.CSVWriter;
import org.hl7.fhir.r5.utils.formats.XLSXWriter; import org.hl7.fhir.r5.utils.formats.XLSXWriter;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.MarkDownProcessor;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.i18n.I18nConstants;
@ -362,9 +363,28 @@ public class ProfileUtilities extends TranslatingUtilities {
public List<ElementDefinition> getChildMap(StructureDefinition profile, ElementDefinition element) throws DefinitionException { public List<ElementDefinition> getChildMap(StructureDefinition profile, ElementDefinition element) throws DefinitionException {
if (element.getContentReference()!=null) { if (element.getContentReference() != null) {
for (ElementDefinition e : profile.getSnapshot().getElement()) { List<ElementDefinition> list = null;
if (element.getContentReference().equals("#"+e.getId())) String id = null;
if (element.getContentReference().startsWith("#")) {
// internal reference
id = element.getContentReference().substring(1);
list = profile.getSnapshot().getElement();
} else if (element.getContentReference().contains("#")) {
// external reference
String ref = element.getContentReference();
StructureDefinition sd = context.fetchResource(StructureDefinition.class, ref.substring(0, ref.indexOf("#")));
if (sd == null) {
throw new DefinitionException("unable to process contentReference '"+element.getContentReference()+"' on element '"+element.getId()+"'");
}
list = sd.getSnapshot().getElement();
id = ref.substring(ref.indexOf("#")+1);
} else {
throw new DefinitionException("unable to process contentReference '"+element.getContentReference()+"' on element '"+element.getId()+"'");
}
for (ElementDefinition e : list) {
if (id.equals(e.getId()))
return getChildMap(profile, e); return getChildMap(profile, e);
} }
throw new DefinitionException(context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_NAME_REFERENCE__AT_PATH_, element.getContentReference(), element.getPath())); throw new DefinitionException(context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_NAME_REFERENCE__AT_PATH_, element.getContentReference(), element.getPath()));
@ -3155,6 +3175,10 @@ public class ProfileUtilities extends TranslatingUtilities {
c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(corePath+"terminologies.html#"+ved.getBinding().getStrength().toCode(), egt(ved.getBinding().getStrengthElement()), ved.getBinding().getStrength().getDefinition()))); c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(corePath+"terminologies.html#"+ved.getBinding().getStrength().toCode(), egt(ved.getBinding().getStrengthElement()), ved.getBinding().getStrength().getDefinition())));
c.getPieces().add(gen.new Piece(null, ")", null)); c.getPieces().add(gen.new Piece(null, ")", null));
} }
if (ved.getBinding().hasDescription() && MarkDownProcessor.isSimpleMarkdown(ved.getBinding().getDescription())) {
c.getPieces().add(gen.new Piece(null, ": ", null));
c.addMarkdownNoPara(ved.getBinding().getDescription());
}
} }
c.addPiece(gen.new Piece("br")).addPiece(gen.new Piece(null, describeExtensionContext(ed), null)); c.addPiece(gen.new Piece("br")).addPiece(gen.new Piece(null, describeExtensionContext(ed), null));
r.getCells().add(c); r.getCells().add(c);
@ -4282,6 +4306,10 @@ public class ProfileUtilities extends TranslatingUtilities {
c.getPieces().add(checkForNoChange(binding, gen.new Piece(corePath+"extension-elementdefinition-minvalueset.html", translate("sd.table", "Min Binding")+": ", "Min Value Set Extension").addStyle("font-weight:bold"))); c.getPieces().add(checkForNoChange(binding, gen.new Piece(corePath+"extension-elementdefinition-minvalueset.html", translate("sd.table", "Min Binding")+": ", "Min Value Set Extension").addStyle("font-weight:bold")));
c.getPieces().add(checkForNoChange(binding, gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !pkp.prependLinks() ? br.url : corePath+br.url, br.display, null))); c.getPieces().add(checkForNoChange(binding, gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !pkp.prependLinks() ? br.url : corePath+br.url, br.display, null)));
} }
if (binding.hasDescription() && MarkDownProcessor.isSimpleMarkdown(binding.getDescription())) {
c.getPieces().add(gen.new Piece(null, ": ", null));
c.addMarkdownNoPara(binding.getDescription());
}
} }
for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) { for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) {
if (!inv.hasSource() || profile == null || inv.getSource().equals(profile.getUrl()) || allInvariants) { if (!inv.hasSource() || profile == null || inv.getSource().equals(profile.getUrl()) || allInvariants) {
@ -4300,7 +4328,6 @@ public class ProfileUtilities extends TranslatingUtilities {
// don't show this, this it's important: c.getPieces().add(gen.new Piece(null, "This repeating element has no defined order", null)); // don't show this, this it's important: c.getPieces().add(gen.new Piece(null, "This repeating element has no defined order", null));
} }
} }
if (definition.hasFixed()) { if (definition.hasFixed()) {
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, translate("sd.table", "Fixed Value")+": ", null).addStyle("font-weight:bold"))); c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, translate("sd.table", "Fixed Value")+": ", null).addStyle("font-weight:bold")));
@ -4594,6 +4621,10 @@ public class ProfileUtilities extends TranslatingUtilities {
c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, " (", null))); c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, " (", null)));
c.getPieces().add(checkForNoChange(binding, gen.new Piece(corePath+"terminologies.html#"+binding.getStrength().toCode(), binding.getStrength().toCode(), binding.getStrength().getDefinition()))); c.getPieces().add(gen.new Piece(null, ")", null)); c.getPieces().add(checkForNoChange(binding, gen.new Piece(corePath+"terminologies.html#"+binding.getStrength().toCode(), binding.getStrength().toCode(), binding.getStrength().getDefinition()))); c.getPieces().add(gen.new Piece(null, ")", null));
} }
if (binding.hasDescription() && MarkDownProcessor.isSimpleMarkdown(binding.getDescription())) {
c.getPieces().add(gen.new Piece(null, ": ", null));
c.addMarkdownNoPara(binding.getDescription());
}
} }
for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) { for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) {
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
@ -5338,12 +5369,12 @@ public class ProfileUtilities extends TranslatingUtilities {
if (!checkFirst || !sd.hasDifferential() || hasMissingIds(sd.getDifferential().getElement())) { if (!checkFirst || !sd.hasDifferential() || hasMissingIds(sd.getDifferential().getElement())) {
if (!sd.hasDifferential()) if (!sd.hasDifferential())
sd.setDifferential(new StructureDefinitionDifferentialComponent()); sd.setDifferential(new StructureDefinitionDifferentialComponent());
generateIds(sd.getDifferential().getElement(), sd.getUrl()); generateIds(sd.getDifferential().getElement(), sd.getUrl(), sd.getType());
} }
if (!checkFirst || !sd.hasSnapshot() || hasMissingIds(sd.getSnapshot().getElement())) { if (!checkFirst || !sd.hasSnapshot() || hasMissingIds(sd.getSnapshot().getElement())) {
if (!sd.hasSnapshot()) if (!sd.hasSnapshot())
sd.setSnapshot(new StructureDefinitionSnapshotComponent()); sd.setSnapshot(new StructureDefinitionSnapshotComponent());
generateIds(sd.getSnapshot().getElement(), sd.getUrl()); generateIds(sd.getSnapshot().getElement(), sd.getUrl(), sd.getType());
} }
} }
@ -5388,11 +5419,10 @@ public class ProfileUtilities extends TranslatingUtilities {
} }
private void generateIds(List<ElementDefinition> list, String name) throws DefinitionException { private void generateIds(List<ElementDefinition> list, String name, String type) throws DefinitionException {
if (list.isEmpty()) if (list.isEmpty())
return; return;
Map<String, String> idMap = new HashMap<String, String>();
Map<String, String> idList = new HashMap<String, String>(); Map<String, String> idList = new HashMap<String, String>();
SliceList sliceInfo = new SliceList(); SliceList sliceInfo = new SliceList();
@ -5420,7 +5450,6 @@ public class ProfileUtilities extends TranslatingUtilities {
} }
} }
String bs = b.toString(); String bs = b.toString();
idMap.put(ed.hasId() ? ed.getId() : ed.getPath(), bs);
ed.setId(bs); ed.setId(bs);
if (idList.containsKey(bs)) { if (idList.containsKey(bs)) {
if (exception || messages == null) { if (exception || messages == null) {
@ -5429,11 +5458,9 @@ public class ProfileUtilities extends TranslatingUtilities {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, name+"."+bs, "Duplicate Element id "+bs, ValidationMessage.IssueSeverity.ERROR)); messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, name+"."+bs, "Duplicate Element id "+bs, ValidationMessage.IssueSeverity.ERROR));
} }
idList.put(bs, ed.getPath()); idList.put(bs, ed.getPath());
if (ed.hasContentReference()) { if (ed.hasContentReference() && ed.getContentReference().startsWith("#")) {
String s = ed.getContentReference().substring(1); String s = ed.getContentReference();
if (idMap.containsKey(s)) ed.setContentReference("http://hl7.org/fhir/StructureDefinition/"+type+s);
ed.setContentReference("#"+idMap.get(s));
} }
} }
// second path - fix up any broken path based id references // second path - fix up any broken path based id references

View File

@ -208,6 +208,9 @@ public class JsonParser extends ParserBase {
JsonElement e = object.get(name); JsonElement e = object.get(name);
if (property.isList() && (e instanceof JsonArray)) { if (property.isList() && (e instanceof JsonArray)) {
JsonArray arr = (JsonArray) e; JsonArray arr = (JsonArray) e;
if (arr.size() == 0) {
logError(line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ARRAY_CANNOT_BE_EMPTY), IssueSeverity.ERROR);
}
int c = 0; int c = 0;
for (JsonElement am : arr) { for (JsonElement am : arr) {
parseChildComplexInstance(npath+"["+c+"]", object, element, property, name, am); parseChildComplexInstance(npath+"["+c+"]", object, element, property, name, am);

View File

@ -46,6 +46,7 @@ public class OperationOutcomeUtilities {
public static OperationOutcomeIssueComponent convertToIssue(ValidationMessage message, OperationOutcome op) { public static OperationOutcomeIssueComponent convertToIssue(ValidationMessage message, OperationOutcome op) {
OperationOutcomeIssueComponent issue = new OperationOutcome.OperationOutcomeIssueComponent(); OperationOutcomeIssueComponent issue = new OperationOutcome.OperationOutcomeIssueComponent();
issue.setUserData("source.vm", message);
issue.setCode(convert(message.getType())); issue.setCode(convert(message.getType()));
if (message.getLocation() != null) { if (message.getLocation() != null) {

View File

@ -104,6 +104,8 @@ public class ProfileUtilitiesTests {
f.setComment(null); f.setComment(null);
b.setDefinition(null); b.setDefinition(null);
f.setDefinition(null); f.setDefinition(null);
b.setContentReference(null);
f.setContentReference(null);
ok = Base.compareDeep(b, f, true); ok = Base.compareDeep(b, f, true);
} }
} }

View File

@ -79,4 +79,9 @@ public class MarkDownProcessor {
return html; return html;
} }
public static boolean isSimpleMarkdown(String description) {
return !description.contains("\n");
}
} }

View File

@ -12,6 +12,7 @@ public class I18nConstants {
public static final String ALL_OBSERVATIONS_SHOULD_HAVE_A_PERFORMER = "All_observations_should_have_a_performer"; public static final String ALL_OBSERVATIONS_SHOULD_HAVE_A_PERFORMER = "All_observations_should_have_a_performer";
public static final String ALL_OBSERVATIONS_SHOULD_HAVE_A_SUBJECT = "All_observations_should_have_a_subject"; public static final String ALL_OBSERVATIONS_SHOULD_HAVE_A_SUBJECT = "All_observations_should_have_a_subject";
public static final String ALL_OK = "ALL_OK"; public static final String ALL_OK = "ALL_OK";
public static final String ARRAY_CANNOT_BE_EMPTY = "ARRAY_CANNOT_BE_EMPTY";
public static final String ATTEMPT_TO_A_SLICE_AN_ELEMENT_THAT_DOES_NOT_REPEAT__FROM__IN_ = "Attempt_to_a_slice_an_element_that_does_not_repeat__from__in_"; public static final String ATTEMPT_TO_A_SLICE_AN_ELEMENT_THAT_DOES_NOT_REPEAT__FROM__IN_ = "Attempt_to_a_slice_an_element_that_does_not_repeat__from__in_";
public static final String ATTEMPT_TO_REPLACE_ELEMENT_NAME_FOR_A_NONCHOICE_TYPE = "Attempt_to_replace_element_name_for_a_nonchoice_type"; public static final String ATTEMPT_TO_REPLACE_ELEMENT_NAME_FOR_A_NONCHOICE_TYPE = "Attempt_to_replace_element_name_for_a_nonchoice_type";
public static final String ATTEMPT_TO_USE_A_SNAPSHOT_ON_PROFILE__AS__BEFORE_IT_IS_GENERATED = "Attempt_to_use_a_snapshot_on_profile__as__before_it_is_generated"; public static final String ATTEMPT_TO_USE_A_SNAPSHOT_ON_PROFILE__AS__BEFORE_IT_IS_GENERATED = "Attempt_to_use_a_snapshot_on_profile__as__before_it_is_generated";

View File

@ -240,6 +240,7 @@ public class HierarchicalTableGenerator extends TranslatingUtilities {
pieces.add(piece); pieces.add(piece);
return this; return this;
} }
public Cell addMarkdown(String md) { public Cell addMarkdown(String md) {
try { try {
Parser parser = Parser.builder().build(); Parser parser = Parser.builder().build();
@ -253,6 +254,19 @@ public class HierarchicalTableGenerator extends TranslatingUtilities {
return this; return this;
} }
public Cell addMarkdownNoPara(String md) {
try {
Parser parser = Parser.builder().build();
Node document = parser.parse(md);
HtmlRenderer renderer = HtmlRenderer.builder().escapeHtml(true).build();
String html = renderer.render(document);
pieces.addAll(htmlToParagraphPieces(html));
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
private List<Piece> htmlToParagraphPieces(String html) { private List<Piece> htmlToParagraphPieces(String html) {
List<Piece> myPieces = new ArrayList<Piece>(); List<Piece> myPieces = new ArrayList<Piece>();
try { try {

View File

@ -209,11 +209,12 @@ Validation_BUNDLE_Message = The first entry in a message must be a MessageHeader
Validation_VAL_Content_Unknown = Unrecognised Content {0} Validation_VAL_Content_Unknown = Unrecognised Content {0}
Validation_VAL_NoType = Unknown type {0} Validation_VAL_NoType = Unknown type {0}
Validation_VAL_Profile_MatchMultiple = Profile {0}, Element matches more than one slice - {1}, {2} Validation_VAL_Profile_MatchMultiple = Profile {0}, Element matches more than one slice - {1}, {2}
Validation_VAL_Profile_Maximum = {0}: max allowed = {1}, but found {2} // for the next 4 messages, the available parameters are: 0: profile url, 1: ed.path, 2: ed.id, 3: ed.sliceName, 4: ed.label, 5: element.path, 6: ed.min and optionally 7: actual count
Validation_VAL_Profile_Minimum = {0}: minimum required = {1}, but only found {2} Validation_VAL_Profile_Maximum = {2}: max allowed = {6}, but found {7} (from {0})
Validation_VAL_Profile_Minimum = {2}: minimum required = {6}, but only found {7} (from {0})
Validation_VAL_Profile_NoCheckMax = {2}: Unable to check max allowed ({1}) due to lack of slicing validation (from {0})
Validation_VAL_Profile_NoCheckMin = {2}: Unable to check minimum required ({1}) due to lack of slicing validation (from {0})
Validation_VAL_Profile_MultipleMatches = Found multiple matching profiles among choices: {0} Validation_VAL_Profile_MultipleMatches = Found multiple matching profiles among choices: {0}
Validation_VAL_Profile_NoCheckMax = {0}: Unable to check max allowed ({1}) due to lack of slicing validation
Validation_VAL_Profile_NoCheckMin = {0}'': Unable to check minimum required ({1}) due to lack of slicing validation
Validation_VAL_Profile_NoDefinition = No definition found for resource type ''{0}'' Validation_VAL_Profile_NoDefinition = No definition found for resource type ''{0}''
Validation_VAL_Profile_NoMatch = Unable to find matching profile among choices: {0} Validation_VAL_Profile_NoMatch = Unable to find matching profile among choices: {0}
Validation_VAL_Profile_NoSnapshot = StructureDefinition has no snapshot - validation is against the snapshot, so it must be provided Validation_VAL_Profile_NoSnapshot = StructureDefinition has no snapshot - validation is against the snapshot, so it must be provided
@ -617,4 +618,5 @@ SD_ED_TYPE_PROFILE_NOTYPE = Found profile {0}, but unable to determine the type
SD_ED_TYPE_PROFILE_WRONG = Profile {0} is for type {1}, but this element has type {2} SD_ED_TYPE_PROFILE_WRONG = Profile {0} is for type {1}, but this element has type {2}
TERMINOLOGY_TX_NOSVC_BOUND_REQ = Could not confirm that the codes provided are from the required value set {0} because there is no terminology service TERMINOLOGY_TX_NOSVC_BOUND_REQ = Could not confirm that the codes provided are from the required value set {0} because there is no terminology service
TERMINOLOGY_TX_NOSVC_BOUND_EXT = Could not confirm that the codes provided are from the extensible value set {0} because there is no terminology service TERMINOLOGY_TX_NOSVC_BOUND_EXT = Could not confirm that the codes provided are from the extensible value set {0} because there is no terminology service
ARRAY_CANNOT_BE_EMPTY = Array cannot be empty - the property should not be present if it has no values

View File

@ -29,6 +29,10 @@ import java.util.UUID;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.hl7.fhir.convertors.VersionConvertorAdvisor50; import org.hl7.fhir.convertors.VersionConvertorAdvisor50;
import org.hl7.fhir.convertors.VersionConvertor_10_30; import org.hl7.fhir.convertors.VersionConvertor_10_30;
@ -56,6 +60,7 @@ import org.hl7.fhir.r5.context.SimpleWorkerContext;
import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.elementmodel.Manager; import org.hl7.fhir.r5.elementmodel.Manager;
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat; import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.r5.elementmodel.ParserBase.ValidationPolicy;
import org.hl7.fhir.r5.elementmodel.ObjectConverter; import org.hl7.fhir.r5.elementmodel.ObjectConverter;
import org.hl7.fhir.r5.formats.FormatUtilities; import org.hl7.fhir.r5.formats.FormatUtilities;
import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.formats.IParser.OutputStyle;
@ -98,19 +103,23 @@ import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.json.JsonTrackingParser;
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.npm.ToolsVersion; import org.hl7.fhir.utilities.npm.ToolsVersion;
import org.hl7.fhir.utilities.turtle.Turtle;
import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; 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.XhtmlComposer; import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
import org.hl7.fhir.validation.BaseValidator.ValidationControl; import org.hl7.fhir.validation.BaseValidator.ValidationControl;
import org.hl7.fhir.validation.ValidationEngine.ValidationRecord;
import org.hl7.fhir.validation.cli.model.ScanOutputItem; import org.hl7.fhir.validation.cli.model.ScanOutputItem;
import org.hl7.fhir.validation.cli.services.StandAloneValidatorFetcher.IPackageInstaller; import org.hl7.fhir.validation.cli.services.StandAloneValidatorFetcher.IPackageInstaller;
import org.hl7.fhir.validation.cli.utils.*; import org.hl7.fhir.validation.cli.utils.*;
import org.hl7.fhir.validation.instance.InstanceValidator; import org.hl7.fhir.validation.instance.InstanceValidator;
import org.w3c.dom.Document;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
@ -187,6 +196,50 @@ POSSIBILITY OF SUCH DAMAGE.
*/ */
public class ValidationEngine implements IValidatorResourceFetcher, IPackageInstaller { public class ValidationEngine implements IValidatorResourceFetcher, IPackageInstaller {
public class ValidationRecord {
private String location;
private List<ValidationMessage> messages;
int err = 0;
int warn = 0;
int info = 0;
public ValidationRecord(String location, List<ValidationMessage> messages) {
this.location = location;
this.messages = messages;
for (ValidationMessage vm : messages) {
if (vm.getLevel().equals(ValidationMessage.IssueSeverity.FATAL)||vm.getLevel().equals(ValidationMessage.IssueSeverity.ERROR))
err++;
else if (vm.getLevel().equals(ValidationMessage.IssueSeverity.WARNING))
warn++;
else if (!vm.isSignpost()) {
info++;
}
}
}
public String getLocation() {
return location;
}
public List<ValidationMessage> getMessages() {
return messages;
}
public int getErr() {
return err;
}
public int getWarn() {
return warn;
}
public int getInfo() {
return info;
}
}
public class TransformSupportServices implements ITransformerServices { public class TransformSupportServices implements ITransformerServices {
private List<Base> outputs; private List<Base> outputs;
@ -538,7 +591,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
throw new FHIRException("Unable to fetch content from "+src+" ("+errors.toString()+")"); throw new FHIRException("Unable to fetch content from "+src+" ("+errors.toString()+")");
} }
FhirFormat fmt = checkIsResource(cnt, src); FhirFormat fmt = checkFormat(cnt, src);
if (fmt != null) { if (fmt != null) {
Map<String, byte[]> res = new HashMap<String, byte[]>(); Map<String, byte[]> res = new HashMap<String, byte[]>();
res.put(Utilities.changeFileExt(src, "."+fmt.getExtension()), cnt); res.put(Utilities.changeFileExt(src, "."+fmt.getExtension()), cnt);
@ -809,33 +862,99 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
this.noInvariantChecks = value; this.noInvariantChecks = value;
} }
private FhirFormat checkFormat(byte[] cnt, String filename) {
System.out.println(" ..Detect format for "+filename);
try {
JsonTrackingParser.parseJson(cnt);
return FhirFormat.JSON;
} catch (Exception e) {
if (debug) {
System.out.println("Not JSON: "+e.getMessage());
}
}
try {
parseXml(cnt);
return FhirFormat.XML;
} catch (Exception e) {
if (debug) {
System.out.println("Not XML: "+e.getMessage());
}
}
try {
new Turtle().parse(TextFile.bytesToString(cnt));
return FhirFormat.TURTLE;
} catch (Exception e) {
if (debug) {
System.out.println("Not Turtle: "+e.getMessage());
}
}
try {
new StructureMapUtilities(context, null, null).parse(TextFile.bytesToString(cnt), null);
return FhirFormat.TEXT;
} catch (Exception e) {
if (debug) {
System.out.println("Not Text: "+e.getMessage());
}
}
if (debug)
System.out.println(" .. not a resource: "+filename);
return null;
}
private FhirFormat checkIsResource(byte[] cnt, String filename) { private FhirFormat checkIsResource(byte[] cnt, String filename) {
System.out.println(" ..Detect format for "+filename); System.out.println(" ..Detect format for "+filename);
try { try {
Manager.parse(context, new ByteArrayInputStream(cnt), FhirFormat.JSON); Manager.parse(context, new ByteArrayInputStream(cnt), FhirFormat.JSON);
return FhirFormat.JSON; return FhirFormat.JSON;
} catch (Exception e) { } catch (Exception e) {
if (debug) {
System.out.println("Not JSON: "+e.getMessage());
}
} }
try { try {
Manager.parse(context, new ByteArrayInputStream(cnt),FhirFormat.XML); parseXml(cnt);
return FhirFormat.XML; return FhirFormat.XML;
} catch (Exception e) { } catch (Exception e) {
if (debug) {
System.out.println("Not XML: "+e.getMessage());
}
} }
try { try {
Manager.parse(context, new ByteArrayInputStream(cnt),FhirFormat.TURTLE); Manager.parse(context, new ByteArrayInputStream(cnt),FhirFormat.TURTLE);
return FhirFormat.TURTLE; return FhirFormat.TURTLE;
} catch (Exception e) { } catch (Exception e) {
if (debug) {
System.out.println("Not Turtle: "+e.getMessage());
}
} }
try { try {
new StructureMapUtilities(context, null, null).parse(TextFile.bytesToString(cnt), null); new StructureMapUtilities(context, null, null).parse(TextFile.bytesToString(cnt), null);
return FhirFormat.TEXT; return FhirFormat.TEXT;
} catch (Exception e) { } catch (Exception e) {
if (debug) {
System.out.println("Not Text: "+e.getMessage());
}
} }
if (debug) if (debug)
System.out.println(" .. not a resource: "+filename); System.out.println(" .. not a resource: "+filename);
return null; return null;
} }
private Document parseXml(byte[] cnt) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// xxe protection
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(new ByteArrayInputStream(cnt));
}
private FhirFormat checkIsResource(String path) throws IOException { private FhirFormat checkIsResource(String path) throws IOException {
String ext = Utilities.getFileExtension(path); String ext = Utilities.getFileExtension(path);
if (Utilities.existsInList(ext, "xml")) if (Utilities.existsInList(ext, "xml"))
@ -1097,7 +1216,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
public OperationOutcome validate(String source, List<String> profiles) throws FHIRException, IOException { public OperationOutcome validate(String source, List<String> profiles) throws FHIRException, IOException {
List<String> l = new ArrayList<String>(); List<String> l = new ArrayList<String>();
l.add(source); l.add(source);
return (OperationOutcome)validate(l, profiles); return (OperationOutcome)validate(l, profiles, null);
} }
public List<ScanOutputItem> validateScan(List<String> sources, Set<String> guides) throws FHIRException, IOException, EOperationOutcome { public List<ScanOutputItem> validateScan(List<String> sources, Set<String> guides) throws FHIRException, IOException, EOperationOutcome {
@ -1193,7 +1312,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
} }
} }
public Resource validate(List<String> sources, List<String> profiles) throws FHIRException, IOException { public Resource validate(List<String> sources, List<String> profiles, List<ValidationRecord> record) throws FHIRException, IOException {
if (profiles.size() > 0) { if (profiles.size() > 0) {
System.out.println(" Profiles: "+profiles); System.out.println(" Profiles: "+profiles);
} }
@ -1207,7 +1326,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
System.out.print(" Validate " + ref); System.out.print(" Validate " + ref);
Content cnt = loadContent(ref, "validate", false); Content cnt = loadContent(ref, "validate", false);
try { try {
OperationOutcome outcome = validate(ref, cnt.focus, cnt.cntType, profiles); OperationOutcome outcome = validate(ref, cnt.focus, cnt.cntType, profiles, record);
ToolingExtensions.addStringExtension(outcome, ToolingExtensions.EXT_OO_FILE, ref); ToolingExtensions.addStringExtension(outcome, ToolingExtensions.EXT_OO_FILE, ref);
System.out.println(" " + context.clock().milestone()); System.out.println(" " + context.clock().milestone());
results.addEntry().setResource(outcome); results.addEntry().setResource(outcome);
@ -1281,7 +1400,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
} }
public OperationOutcome validate(String location, byte[] source, FhirFormat cntType, List<String> profiles) throws FHIRException, IOException, EOperationOutcome, SAXException { public OperationOutcome validate(String location, byte[] source, FhirFormat cntType, List<String> profiles, List<ValidationRecord> record) throws FHIRException, IOException, EOperationOutcome, SAXException {
List<ValidationMessage> messages = new ArrayList<ValidationMessage>(); List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
if (doNative) { if (doNative) {
SchemaValidator.validateSchema(location, cntType, messages); SchemaValidator.validateSchema(location, cntType, messages);
@ -1291,6 +1410,9 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
if (showTimes) { if (showTimes) {
System.out.println(location+": "+validator.reportTimes()); System.out.println(location+": "+validator.reportTimes());
} }
if (record != null) {
record.add(new ValidationRecord(location, messages));
}
return messagesToOutcome(messages); return messagesToOutcome(messages);
} }

View File

@ -41,6 +41,8 @@ public class CliContext {
private String map = null; private String map = null;
@JsonProperty("output") @JsonProperty("output")
private String output = null; private String output = null;
@JsonProperty("htmlOutput")
private String htmlOutput = null;
@JsonProperty("txServer") @JsonProperty("txServer")
private String txServer = "http://tx.fhir.org"; private String txServer = "http://tx.fhir.org";
@JsonProperty("sv") @JsonProperty("sv")
@ -252,6 +254,17 @@ public class CliContext {
return this; return this;
} }
@JsonProperty("output")
public String getHtmlOutput() {
return htmlOutput;
}
@JsonProperty("output")
public CliContext setHtmlOutput(String htmlOutput) {
this.htmlOutput = htmlOutput;
return this;
}
@JsonProperty("canDoNative") @JsonProperty("canDoNative")
public boolean getCanDoNative() { public boolean getCanDoNative() {
return canDoNative; return canDoNative;
@ -472,6 +485,7 @@ public class CliContext {
noExtensibleBindingMessages == that.noExtensibleBindingMessages && noExtensibleBindingMessages == that.noExtensibleBindingMessages &&
Objects.equals(map, that.map) && Objects.equals(map, that.map) &&
Objects.equals(output, that.output) && Objects.equals(output, that.output) &&
Objects.equals(htmlOutput, that.htmlOutput) &&
Objects.equals(txServer, that.txServer) && Objects.equals(txServer, that.txServer) &&
Objects.equals(sv, that.sv) && Objects.equals(sv, that.sv) &&
Objects.equals(txLog, that.txLog) && Objects.equals(txLog, that.txLog) &&
@ -493,7 +507,7 @@ public class CliContext {
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(doNative, anyExtensionsAllowed, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching, noExtensibleBindingMessages, map, output, txServer, sv, txLog, mapLog, lang, fhirpath, snomedCT, targetVer, igs, questionnaireMode, profiles, sources, mode, locale, locations, crumbTrails, showTimes); return Objects.hash(doNative, anyExtensionsAllowed, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching, noExtensibleBindingMessages, map, output, htmlOutput, txServer, sv, txLog, mapLog, lang, fhirpath, snomedCT, targetVer, igs, questionnaireMode, profiles, sources, mode, locale, locations, crumbTrails, showTimes);
} }
@Override @Override
@ -510,6 +524,7 @@ public class CliContext {
", noExtensibleBindingMessages=" + noExtensibleBindingMessages + ", noExtensibleBindingMessages=" + noExtensibleBindingMessages +
", map='" + map + '\'' + ", map='" + map + '\'' +
", output='" + output + '\'' + ", output='" + output + '\'' +
", htmlOutput='" + htmlOutput + '\'' +
", txServer='" + txServer + '\'' + ", txServer='" + txServer + '\'' +
", sv='" + sv + '\'' + ", sv='" + sv + '\'' +
", txLog='" + txLog + '\'' + ", txLog='" + txLog + '\'' +

View File

@ -0,0 +1,181 @@
package org.hl7.fhir.validation.cli.services;
import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.validation.ValidationEngine.ValidationRecord;
import org.hl7.fhir.validation.cli.utils.VersionUtil;
public class HTMLOutputGenerator {
private List<ValidationRecord> records;
public HTMLOutputGenerator(List<ValidationRecord> records) {
super();
this.records = records;
}
public String generate(long time) {
StringBuilder b = new StringBuilder();
b.append(genHeader(time));
int i = 0;
for (ValidationRecord f : records) {
i++;
b.append(genSummaryRow(i, f));
}
b.append("</table>\r\n");
i = 0;
int id = 0;
for (ValidationRecord f : records) {
i++;
b.append(genStart(i, f));
if (f.getMessages().size() > 0) {
b.append(
" <table class=\"grid\">\r\n"+
" <tr>\r\n"+
" <td><b>Path</b></td><td><b>Severity</b></td><td><b>Message</b></td>\r\n"+
" </tr>\r\n");
for (ValidationMessage vm : f.getMessages()) {
id++;
b.append(genDetails(vm, "m"+id));
}
b.append("</table>\r\n");
} else {
b.append("<p>No Issues detected</p>\r\n");
}
}
return b.toString();
}
private String genHeader(long time) {
int err = 0;
int warn = 0;
int info = 0;
for (ValidationRecord f : records) {
err = err + f.getErr();
warn = warn + f.getWarn();
info = info + f.getInfo();
}
return
"<!DOCTYPE HTML>\r\n"+
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\r\n"+
"<head>\r\n"+
" <title>Validation Results</title>\r\n"+
" <link href=\"http://hl7.org/fhir/fhir.css\" rel=\"stylesheet\"/>\r\n"+
" <style>\r\n"+
" span.flip { background-color: #4CAF50; color: white; border: solid 1px #a6d8a8; padding: 2px }\r\n"+
" </style>\r\n"+
" <script>\r\n"+
" function flip(id) {\r\n"+
" var span = document.getElementById('s'+id);\r\n"+
" var div = document.getElementById(id);\r\n"+
" if (document.getElementById('s'+id).innerHTML == 'Show Reasoning') {\r\n"+
" div.style.display = 'block';\r\n"+
" span.innerHTML = 'Hide Reasoning';\r\n"+
" } else {\r\n"+
" div.style.display = 'none';\r\n"+
" span.innerHTML = 'Show Reasoning';\r\n"+
" }\r\n"+
" }\r\n"+
" </script>\r\n"+
"</head>\r\n"+
"<body style=\"margin: 20px; background-color: #ffffff\">\r\n"+
" <h1>Validation Results</h1>\r\n"+
" <p>"+err+" "+Utilities.pluralize("error", err)+", "+warn+" "+Utilities.pluralize("warning", warn)+", "+info+" "+Utilities.pluralize("hint", info)+". Generated "+now()+" by Validator "+VersionUtil.getVersionString()+" ("+time+"ms)</p>\r\n"+
" <table class=\"grid\">\r\n"+
" <tr>\r\n"+
" <td><b>Filename</b></td><td><b>Errors</b></td><td><b>Warnings</b></td><td><b>Hints</b></td>\r\n"+
" </tr>\r\n";
}
private String now() {
return DateFormat.getDateTimeInstance().format(new Date());
}
private String genSummaryRow(int i, ValidationRecord rec) {
String color = colorForLevel(IssueSeverity.ERROR, false);
if (rec.getErr() == 0) {
color = "#EFFFEF";
}
return
" <tr style=\"background-color: "+color+"\">\r\n"+
" <td><a href=\"#;"+i+"\"><b>"+Utilities.escapeXml(rec.getLocation())+"</b></a></td><td><b>"+rec.getErr()+"</b></td><td><b>"+rec.getWarn()+"</b></td><td><b>"+rec.getInfo()+"</b></td>\r\n"+
" </tr>\r\n";
}
private String genStart(int i, ValidationRecord f) {
String xlink = Utilities.isAbsoluteUrl(f.getLocation()) ? f.getLocation() : "file:"+f.getLocation();
return
"<hr/>\r\n"+
"<a name=\"l"+i+"\"> </a>\r\n"+
"<h2><a href=\""+xlink+"\">"+Utilities.escapeXml(f.getLocation())+"</a></h2>\r\n";
}
private String genDetails(ValidationMessage vm, String id) {
String path = vm.getLocation() == null ? "" : vm.getLocation()+ lineCol(vm);
String level = vm.isSlicingHint() ? "Slicing Information" : vm.isSignpost() ? "Process Info" : vm.getLevel().toCode();
String color = colorForLevel(vm.getLevel(), vm.isSignpost());
String mid = vm.getMessageId();
String msg = vm.getHtml();
String msgdetails = vm.isSlicingHint() ? vm.getSliceHtml() : vm.getHtml();
if (vm.isSlicingHint()) {
return
" <tr style=\"background-color: "+color+"\">\r\n"+
" <td><b>"+path+"</b></td><td><b>"+level+"</b></td><td><b>"+msg+"</b> <span id=\"s"+id+"\" class=\"flip\" onclick=\"flip('"+id+"')\">Show Reasoning</span><div id=\""+id+"\" style=\"display: none\"><p>&nbsp;</p>"+msgdetails+"</div></td>\r\n"+
" </tr>\r\n";
} else {
return
" <tr style=\"background-color: "+color+"\">\r\n"+
" <td><b>"+path+"</b></td><td><b>"+level+"</b></td><td title=\""+mid+"\"><b>"+msg+"</b></td>\r\n"+
" </tr>\r\n";
}
}
private String lineCol(ValidationMessage vm) {
return vm.getLine() > 0 ? " (l"+vm.getLine()+"/c"+vm.getCol()+")" : "";
}
private String colorForLevel(IssueSeverity level, boolean signpost) {
if (signpost) {
return "#d6feff";
}
switch (level) {
case ERROR:
return "#ffcccc";
case FATAL:
return "#ff9999";
case WARNING:
return "#ffebcc";
default: // INFORMATION:
return "#ffffe6";
}
}
private String halfColorForLevel(IssueSeverity level, boolean signpost) {
if (signpost) {
return "#e3feff";
}
switch (level) {
case ERROR:
return "#ffeeee";
case FATAL:
return "#ffcccc";
case WARNING:
return "#fff4ee";
default: // INFORMATION:
return "#fffff2";
}
}
}

View File

@ -27,6 +27,7 @@ import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.validation.ValidationEngine; import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.ValidationEngine.ValidationRecord;
import org.hl7.fhir.validation.cli.model.*; import org.hl7.fhir.validation.cli.model.*;
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;
@ -69,7 +70,9 @@ public class ValidationService {
} }
public static void validateSources(CliContext cliContext, ValidationEngine validator) throws Exception { public static void validateSources(CliContext cliContext, ValidationEngine validator) throws Exception {
Resource r = validator.validate(cliContext.getSources(), cliContext.getProfiles()); long start = System.currentTimeMillis();
List<ValidationRecord> records = new ArrayList<>();
Resource r = validator.validate(cliContext.getSources(), cliContext.getProfiles(), records);
int ec = 0; int ec = 0;
System.out.println("Done. "+validator.getContext().clock().report()); System.out.println("Done. "+validator.getContext().clock().report());
System.out.println(); System.out.println();
@ -92,6 +95,11 @@ public class ValidationService {
x.compose(s, r); x.compose(s, r);
s.close(); s.close();
} }
if (cliContext.getHtmlOutput() != null) {
String html = new HTMLOutputGenerator(records).generate(System.currentTimeMillis()-start);
TextFile.stringToFile(html, cliContext.getHtmlOutput());
System.out.println("HTML Summary in "+cliContext.getHtmlOutput());
}
System.exit(ec > 0 ? 1 : 0); System.exit(ec > 0 ? 1 : 0);
} }
@ -288,10 +296,12 @@ public class ValidationService {
System.out.println("Scanning for versions (no -version parameter):"); System.out.println("Scanning for versions (no -version parameter):");
VersionSourceInformation versions = ValidationService.scanForVersions(cliContext); VersionSourceInformation versions = ValidationService.scanForVersions(cliContext);
for (String s : versions.getReport()) { for (String s : versions.getReport()) {
if (!s.equals("(nothing found)")) {
System.out.println(" " + s); System.out.println(" " + s);
} }
}
if (versions.isEmpty()) { if (versions.isEmpty()) {
System.out.println("-> Using Default version '" + VersionUtilities.CURRENT_VERSION + "'"); System.out.println(" No Version Info found: Using Default version '" + VersionUtilities.CURRENT_VERSION + "'");
return "current"; return "current";
} }
if (versions.size() == 1) { if (versions.size() == 1) {

View File

@ -12,6 +12,7 @@ public class Params {
public static final String VERSION = "-version"; public static final String VERSION = "-version";
public static final String OUTPUT = "-output"; public static final String OUTPUT = "-output";
public static final String HTML_OUTPUT = "-html-output";
public static final String PROXY = "-proxy"; public static final String PROXY = "-proxy";
public static final String PROFILE = "-profile"; public static final String PROFILE = "-profile";
public static final String BUNDLE = "-bundle"; public static final String BUNDLE = "-bundle";
@ -93,6 +94,11 @@ public class Params {
throw new Error("Specified -output without indicating output file"); throw new Error("Specified -output without indicating output file");
else else
cliContext.setOutput(args[++i]); cliContext.setOutput(args[++i]);
} else if (args[i].equals(HTML_OUTPUT)) {
if (i + 1 == args.length)
throw new Error("Specified -html-output without indicating output file");
else
cliContext.setHtmlOutput(args[++i]);
} else if (args[i].equals(PROXY)) { } else if (args[i].equals(PROXY)) {
i++; // ignore next parameter i++; // ignore next parameter
} else if (args[i].equals(PROFILE)) { } else if (args[i].equals(PROFILE)) {

View File

@ -961,7 +961,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else { } else {
if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, codings.size() == fixed.getCoding().size(), I18nConstants.TERMINOLOGY_TX_CODING_COUNT, Integer.toString(fixed.getCoding().size()), Integer.toString(codings.size()))) { if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, codings.size() == fixed.getCoding().size(), I18nConstants.TERMINOLOGY_TX_CODING_COUNT, Integer.toString(fixed.getCoding().size()), Integer.toString(codings.size()))) {
for (int i = 0; i < codings.size(); i++) for (int i = 0; i < codings.size(); i++)
checkFixedValue(errors, path + ".coding", codings.get(i), fixed.getCoding().get(i), fixedSource, "coding", focus); checkFixedValue(errors, path + ".coding", codings.get(i), fixed.getCoding().get(i), fixedSource, "coding", focus, false);
} }
} }
} }
@ -1748,10 +1748,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return b.toString(); return b.toString();
} }
private void checkFixedValue(List<ValidationMessage> errors, String path, Element focus, org.hl7.fhir.r5.model.Element fixed, String fixedSource, String propName, Element parent) {
checkFixedValue(errors, path, focus, fixed, fixedSource, propName, parent, false);
}
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
private void checkFixedValue(List<ValidationMessage> errors, String path, Element focus, org.hl7.fhir.r5.model.Element fixed, String fixedSource, String propName, Element parent, boolean pattern) { private void checkFixedValue(List<ValidationMessage> errors, String path, Element focus, org.hl7.fhir.r5.model.Element fixed, String fixedSource, String propName, Element parent, boolean pattern) {
if ((fixed == null || fixed.isEmpty()) && focus == null) { if ((fixed == null || fixed.isEmpty()) && focus == null) {
@ -1829,7 +1825,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
for (Extension e : fixed.getExtension()) { for (Extension e : fixed.getExtension()) {
Element ex = getExtensionByUrl(extensions, e.getUrl()); Element ex = getExtensionByUrl(extensions, e.getUrl());
if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, ex != null, I18nConstants.EXTENSION_EXT_COUNT_NOTFOUND, e.getUrl())) { if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, ex != null, I18nConstants.EXTENSION_EXT_COUNT_NOTFOUND, e.getUrl())) {
checkFixedValue(errors, path, ex.getNamedChild("extension").getNamedChild("value"), e.getValue(), fixedSource, "extension.value", ex.getNamedChild("extension")); checkFixedValue(errors, path, ex.getNamedChild("extension").getNamedChild("value"), e.getValue(), fixedSource, "extension.value", ex.getNamedChild("extension"), false);
} }
} }
} }
@ -1842,27 +1838,35 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
checkFixedValue(errors, path + ".period", focus.getNamedChild("period"), fixed.getPeriod(), fixedSource, "period", focus, pattern); checkFixedValue(errors, path + ".period", focus.getNamedChild("period"), fixed.getPeriod(), fixedSource, "period", focus, pattern);
List<Element> parts = new ArrayList<Element>(); List<Element> parts = new ArrayList<Element>();
if (!pattern || fixed.hasFamily()) {
focus.getNamedChildren("family", parts); focus.getNamedChildren("family", parts);
if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() > 0 == fixed.hasFamily(), I18nConstants.FIXED_TYPE_CHECKS_DT_NAME_FAMILY, (fixed.hasFamily() ? "1" : "0"), Integer.toString(parts.size()))) { if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() > 0 == fixed.hasFamily(), I18nConstants.FIXED_TYPE_CHECKS_DT_NAME_FAMILY, (fixed.hasFamily() ? "1" : "0"), Integer.toString(parts.size()))) {
for (int i = 0; i < parts.size(); i++) for (int i = 0; i < parts.size(); i++)
checkFixedValue(errors, path + ".family", parts.get(i), fixed.getFamilyElement(), fixedSource, "family", focus, pattern); checkFixedValue(errors, path + ".family", parts.get(i), fixed.getFamilyElement(), fixedSource, "family", focus, pattern);
} }
}
if (!pattern || fixed.hasGiven()) {
focus.getNamedChildren("given", parts); focus.getNamedChildren("given", parts);
if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getGiven().size(), I18nConstants.FIXED_TYPE_CHECKS_DT_NAME_GIVEN, Integer.toString(fixed.getGiven().size()), Integer.toString(parts.size()))) { if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getGiven().size(), I18nConstants.FIXED_TYPE_CHECKS_DT_NAME_GIVEN, Integer.toString(fixed.getGiven().size()), Integer.toString(parts.size()))) {
for (int i = 0; i < parts.size(); i++) for (int i = 0; i < parts.size(); i++)
checkFixedValue(errors, path + ".given", parts.get(i), fixed.getGiven().get(i), fixedSource, "given", focus, pattern); checkFixedValue(errors, path + ".given", parts.get(i), fixed.getGiven().get(i), fixedSource, "given", focus, pattern);
} }
}
if (!pattern || fixed.hasPrefix()) {
focus.getNamedChildren("prefix", parts); focus.getNamedChildren("prefix", parts);
if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getPrefix().size(), I18nConstants.FIXED_TYPE_CHECKS_DT_NAME_PREFIX, Integer.toString(fixed.getPrefix().size()), Integer.toString(parts.size()))) { if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getPrefix().size(), I18nConstants.FIXED_TYPE_CHECKS_DT_NAME_PREFIX, Integer.toString(fixed.getPrefix().size()), Integer.toString(parts.size()))) {
for (int i = 0; i < parts.size(); i++) for (int i = 0; i < parts.size(); i++)
checkFixedValue(errors, path + ".prefix", parts.get(i), fixed.getPrefix().get(i), fixedSource, "prefix", focus, pattern); checkFixedValue(errors, path + ".prefix", parts.get(i), fixed.getPrefix().get(i), fixedSource, "prefix", focus, pattern);
} }
}
if (!pattern || fixed.hasSuffix()) {
focus.getNamedChildren("suffix", parts); focus.getNamedChildren("suffix", parts);
if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getSuffix().size(), I18nConstants.FIXED_TYPE_CHECKS_DT_NAME_SUFFIX, Integer.toString(fixed.getSuffix().size()), Integer.toString(parts.size()))) { if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getSuffix().size(), I18nConstants.FIXED_TYPE_CHECKS_DT_NAME_SUFFIX, Integer.toString(fixed.getSuffix().size()), Integer.toString(parts.size()))) {
for (int i = 0; i < parts.size(); i++) for (int i = 0; i < parts.size(); i++)
checkFixedValue(errors, path + ".suffix", parts.get(i), fixed.getSuffix().get(i), fixedSource, "suffix", focus, pattern); checkFixedValue(errors, path + ".suffix", parts.get(i), fixed.getSuffix().get(i), fixedSource, "suffix", focus, pattern);
} }
} }
}
private void checkIdentifier(List<ValidationMessage> errors, String path, Element element, ElementDefinition context) { private void checkIdentifier(List<ValidationMessage> errors, String path, Element element, ElementDefinition context) {
String system = element.getNamedChildValue("system"); String system = element.getNamedChildValue("system");
@ -3054,9 +3058,22 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// work back through the parent list. // work back through the parent list.
// really, there should only be one level for this (contained resources cannot contain // really, there should only be one level for this (contained resources cannot contain
// contained resources), but we'll leave that to some other code to worry about // contained resources), but we'll leave that to some other code to worry about
boolean wasContained = false;
while (stack != null && stack.getElement() != null) { while (stack != null && stack.getElement() != null) {
if (stack.getElement().getProperty().isResource()) { if (stack.getElement().getProperty().isResource()) {
// ok, we'll try to find the contained reference // ok, we'll try to find the contained reference
if (ref.equals("#") && stack.getElement().getSpecial() != SpecialElement.CONTAINED && wasContained) {
ResolvedReference rr = new ResolvedReference();
rr.setResource(stack.getElement());
rr.setFocus(stack.getElement());
rr.setExternal(false);
rr.setStack(stack.push(stack.getElement(), -1, stack.getElement().getProperty().getDefinition(), stack.getElement().getProperty().getDefinition()));
rr.getStack().qualifyPath(".ofType("+stack.getElement().fhirType()+")");
return rr;
}
if (stack.getElement().getSpecial() == SpecialElement.CONTAINED) {
wasContained = true;
}
IndexedElement res = getContainedById(stack.getElement(), ref.substring(1)); IndexedElement res = getContainedById(stack.getElement(), ref.substring(1));
if (res != null) { if (res != null) {
ResolvedReference rr = new ResolvedReference(); ResolvedReference rr = new ResolvedReference();
@ -4069,8 +4086,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// check type invariants // check type invariants
checkInvariants(hostContext, errors, profile, definition, resource, element, stack, false); checkInvariants(hostContext, errors, profile, definition, resource, element, stack, false);
if (definition.getFixed() != null) if (definition.getFixed() != null) {
checkFixedValue(errors, stack.getLiteralPath(), element, definition.getFixed(), profile.getUrl(), definition.getSliceName(), null); checkFixedValue(errors, stack.getLiteralPath(), element, definition.getFixed(), profile.getUrl(), definition.getSliceName(), null, false);
}
if (definition.getPattern() != null) {
checkFixedValue(errors, stack.getLiteralPath(), element, definition.getPattern(), profile.getUrl(), definition.getSliceName(), null, true);
}
// get the list of direct defined children, including slices // get the list of direct defined children, including slices
List<ElementDefinition> childDefinitions = profileUtilities.getChildMap(profile, definition); List<ElementDefinition> childDefinitions = profileUtilities.getChildMap(profile, definition);
@ -4136,13 +4157,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, ElementInfo ei, String extensionUrl) Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, ElementInfo ei, String extensionUrl)
throws FHIRException, DefinitionException { throws FHIRException, DefinitionException {
if (debug && ei.definition != null && ei.slice != null) {
System.out.println(Utilities.padLeft("", ' ', stack.depth())+ "Check "+ei.getPath()+" against both "+ei.definition.getId()+" and "+ei.slice.getId());
}
if (ei.definition != null) { if (ei.definition != null) {
checkChildByDefinition(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, if (debug) {
extensionUrl, ei.definition, false); System.out.println(Utilities.padLeft("", ' ', stack.depth())+ "Check "+ei.getPath()+" against defn "+ei.definition.getId());
}
checkChildByDefinition(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl, ei.definition, false);
} }
if (ei.slice != null) { if (ei.slice != null) {
checkChildByDefinition(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, if (debug) {
extensionUrl, ei.slice, true); System.out.println(Utilities.padLeft("", ' ', stack.depth())+ "Check "+ei.getPath()+" against slice "+ei.slice.getId());
}
checkChildByDefinition(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl, ei.slice, true);
} }
} }
@ -4186,11 +4214,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else if (checkDefn.getType().size() > 1) { } else if (checkDefn.getType().size() > 1) {
String prefix = tail(checkDefn.getPath()); String prefix = tail(checkDefn.getPath());
assert typesAreAllReference(checkDefn.getType()) || checkDefn.hasRepresentation(PropertyRepresentation.TYPEATTR) || prefix.endsWith("[x]") : prefix; assert typesAreAllReference(checkDefn.getType()) || checkDefn.hasRepresentation(PropertyRepresentation.TYPEATTR) || prefix.endsWith("[x]") || isResourceAndTypes(checkDefn) : "Multiple Types allowed, but name is wrong @ "+checkDefn.getPath()+": "+checkDefn.typeSummaryVB();
if (checkDefn.hasRepresentation(PropertyRepresentation.TYPEATTR)) if (checkDefn.hasRepresentation(PropertyRepresentation.TYPEATTR)) {
type = ei.getElement().getType(); type = ei.getElement().getType();
else { } else if (ei.getElement().isResource()) {
type = ei.getElement().fhirType();
} else {
prefix = prefix.substring(0, prefix.length() - 3); prefix = prefix.substring(0, prefix.length() - 3);
for (TypeRefComponent t : checkDefn.getType()) for (TypeRefComponent t : checkDefn.getType())
if ((prefix + Utilities.capitalize(t.getWorkingCode())).equals(ei.getName())) { if ((prefix + Utilities.capitalize(t.getWorkingCode())).equals(ei.getName())) {
@ -4251,7 +4281,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
checkPrimitive(hostContext, errors, ei.getPath(), type, checkDefn, ei.getElement(), profile, stack); checkPrimitive(hostContext, errors, ei.getPath(), type, checkDefn, ei.getElement(), profile, stack);
} else { } else {
if (checkDefn.hasFixed()) { if (checkDefn.hasFixed()) {
checkFixedValue(errors, ei.getPath(), ei.getElement(), checkDefn.getFixed(), profile.getUrl(), checkDefn.getSliceName(), null); checkFixedValue(errors, ei.getPath(), ei.getElement(), checkDefn.getFixed(), profile.getUrl(), checkDefn.getSliceName(), null, false);
} }
if (checkDefn.hasPattern()) { if (checkDefn.hasPattern()) {
checkFixedValue(errors, ei.getPath(), ei.getElement(), checkDefn.getPattern(), profile.getUrl(), checkDefn.getSliceName(), null, true); checkFixedValue(errors, ei.getPath(), ei.getElement(), checkDefn.getPattern(), profile.getUrl(), checkDefn.getSliceName(), null, true);
@ -4386,6 +4416,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
private boolean isResourceAndTypes(ElementDefinition ed) {
if (!Utilities.existsInList(ed.getBase().getPath(), "Bundle.entry.resource", "Bundle.entry.response.outcome", "DomainResource.contained", "Parameters.parameter.resource", "Parameters.parameter.part.resource")) {
return false;
}
for (TypeRefComponent tr : ed.getType()) {
if (!isResource(tr.getCode())) {
return false;
}
}
return true;
}
private boolean isResource(String type) { private boolean isResource(String type) {
StructureDefinition sd = context.fetchTypeDefinition(type); StructureDefinition sd = context.fetchTypeDefinition(type);
return sd != null && sd.getKind().equals(StructureDefinitionKind.RESOURCE); return sd != null && sd.getKind().equals(StructureDefinitionKind.RESOURCE);
@ -4476,18 +4518,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
} }
String location = "Profile " + profile.getUrl() + ", Element '" + stack.getLiteralPath() + "." + tail(ed.getPath()) + (ed.hasSliceName() ? "[" + ed.getSliceName() + (ed.hasLabel() ? " (" + ed.getLabel() + ")" : "") + "]" : "") + "'";
if (ed.getMin() > 0) { if (ed.getMin() > 0) {
if (problematicPaths.contains(ed.getPath())) if (problematicPaths.contains(ed.getPath()))
hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), I18nConstants.VALIDATION_VAL_PROFILE_NOCHECKMIN, location, Integer.toString(ed.getMin())); hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), I18nConstants.VALIDATION_VAL_PROFILE_NOCHECKMIN, profile.getUrl(), ed.getPath(), ed.getId(), ed.getSliceName(),ed.getLabel(), stack.getLiteralPath(), Integer.toString(ed.getMin()));
else else
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), I18nConstants.VALIDATION_VAL_PROFILE_MINIMUM, location, Integer.toString(ed.getMin()), Integer.toString(count)); rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), I18nConstants.VALIDATION_VAL_PROFILE_MINIMUM, profile.getUrl(), ed.getPath(), ed.getId(), ed.getSliceName(),ed.getLabel(), stack.getLiteralPath(), Integer.toString(ed.getMin()), Integer.toString(count));
} }
if (ed.hasMax() && !ed.getMax().equals("*")) { if (ed.hasMax() && !ed.getMax().equals("*")) {
if (problematicPaths.contains(ed.getPath())) if (problematicPaths.contains(ed.getPath()))
hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()), I18nConstants.VALIDATION_VAL_PROFILE_NOCHECKMAX, location, ed.getMax()); hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()), I18nConstants.VALIDATION_VAL_PROFILE_NOCHECKMAX, profile.getUrl(), ed.getPath(), ed.getId(), ed.getSliceName(),ed.getLabel(), stack.getLiteralPath(), ed.getMax());
else else if (count > Integer.parseInt(ed.getMax())) {
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()), I18nConstants.VALIDATION_VAL_PROFILE_MAXIMUM, location, ed.getMax(), Integer.toString(count)); rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_MAXIMUM, profile.getUrl(), ed.getPath(), ed.getId(), ed.getSliceName(),ed.getLabel(), stack.getLiteralPath(), ed.getMax(), Integer.toString(count));
}
} }
} }
} }
@ -4863,7 +4905,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private boolean valueMatchesCriteria(Element value, ElementDefinition criteria, StructureDefinition profile) throws FHIRException { private boolean valueMatchesCriteria(Element value, ElementDefinition criteria, StructureDefinition profile) throws FHIRException {
if (criteria.hasFixed()) { if (criteria.hasFixed()) {
List<ValidationMessage> msgs = new ArrayList<ValidationMessage>(); List<ValidationMessage> msgs = new ArrayList<ValidationMessage>();
checkFixedValue(msgs, "{virtual}", value, criteria.getFixed(), profile.getUrl(), "value", null); checkFixedValue(msgs, "{virtual}", value, criteria.getFixed(), profile.getUrl(), "value", null, false);
return msgs.size() == 0; return msgs.size() == 0;
} else if (criteria.hasBinding() && criteria.getBinding().getStrength() == BindingStrength.REQUIRED && criteria.getBinding().hasValueSet()) { } else if (criteria.hasBinding() && criteria.getBinding().getStrength() == BindingStrength.REQUIRED && criteria.getBinding().hasValueSet()) {
throw new FHIRException(context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SLICE_MATCHING__SLICE_MATCHING_BY_VALUE_SET_NOT_DONE)); throw new FHIRException(context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SLICE_MATCHING__SLICE_MATCHING_BY_VALUE_SET_NOT_DONE));
@ -4983,6 +5025,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if ("(component.empty() and related.empty()) implies (dataAbsentReason or value)".equals(expr)) if ("(component.empty() and related.empty()) implies (dataAbsentReason or value)".equals(expr))
return "(component.empty() and related.empty()) implies (dataAbsentReason.exists() or value.exists())"; return "(component.empty() and related.empty()) implies (dataAbsentReason.exists() or value.exists())";
if ("reference.startsWith('#').not() or (reference.substring(1).trace('url') in %rootResource.contained.id.trace('ids'))".equals(expr)) {
return "(reference = '#') or reference.startsWith('#').not() or (reference.substring(1).trace('url') in %rootResource.contained.id.trace('ids'))";
}
if ("reference.startsWith('#').not() or (reference.substring(1).trace('url') in %resource.contained.id.trace('ids'))".equals(expr)) {
return "(reference = '#') or reference.startsWith('#').not() or (reference.substring(1).trace('url') in %resource.contained.id.trace('ids'))";
}
if ("".equals(expr)) if ("".equals(expr))
return ""; return "";
return expr; return expr;

View File

@ -165,11 +165,25 @@ public class StructureDefinitionValidator extends BaseValidator {
if (t == null) { if (t == null) {
rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), code.equals(t), I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), code.equals(t), I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p);
} else { } else {
rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), code.equals(t), I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code); rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), isInstanceOf(t, code), I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code);
} }
} }
} }
private boolean isInstanceOf(String t, String code) {
StructureDefinition sd = context.fetchTypeDefinition(t);
while (sd != null) {
if (sd.getType().equals(code)) {
return true;
}
sd = sd.hasBaseDefinition() ? context.fetchResource(StructureDefinition.class, sd.getBaseDefinition()) : null;
if (sd != null && !sd.getAbstract()) {
sd = null;
}
}
return false;
}
private String determineBaseType(StructureDefinition sd) { private String determineBaseType(StructureDefinition sd) {
while (sd != null && !sd.hasType() && sd.getDerivation() == TypeDerivationRule.CONSTRAINT) { while (sd != null && !sd.hasType() && sd.getDerivation() == TypeDerivationRule.CONSTRAINT) {
sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition()); sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());

View File

@ -187,5 +187,13 @@ public class NodeStack {
return literalPath; return literalPath;
} }
public int depth() {
if (parent == null) {
return 0;
} else {
return parent.depth()+1;
}
}
} }

View File

@ -90,7 +90,7 @@ public class ValidationEngineTests {
} }
if (!org.hl7.fhir.validation.tests.utilities.TestUtilities.silent) if (!org.hl7.fhir.validation.tests.utilities.TestUtilities.silent)
System.out.println("Test102: Validate patient-example.xml in v1.0.2 version"); System.out.println("Test102: Validate patient-example.xml in v1.0.2 version");
ValidationEngine ve = new ValidationEngine("hl7.fhir.r2.core#1.0.2", DEF_TX, null, FhirPublication.DSTU2, "41.0.2"); ValidationEngine ve = new ValidationEngine("hl7.fhir.r2.core#1.0.2", DEF_TX, null, FhirPublication.DSTU2, "1.0.2");
ve.setNoInvariantChecks(true); ve.setNoInvariantChecks(true);
OperationOutcome op = ve.validate(FhirFormat.XML, TestingUtilities.loadTestResourceStream("validator", "patient102.xml"), null); OperationOutcome op = ve.validate(FhirFormat.XML, TestingUtilities.loadTestResourceStream("validator", "patient102.xml"), null);
if (!TestUtilities.silent) if (!TestUtilities.silent)

View File

@ -19,7 +19,7 @@
<properties> <properties>
<hapi_fhir_version>5.1.0</hapi_fhir_version> <hapi_fhir_version>5.1.0</hapi_fhir_version>
<validator_test_case_version>1.1.46</validator_test_case_version> <validator_test_case_version>1.1.47-SNAPSHOT</validator_test_case_version>
<junit_jupiter_version>5.6.2</junit_jupiter_version> <junit_jupiter_version>5.6.2</junit_jupiter_version>
<maven_surefire_version>3.0.0-M4</maven_surefire_version> <maven_surefire_version>3.0.0-M4</maven_surefire_version>
<jacoco_version>0.8.5</jacoco_version> <jacoco_version>0.8.5</jacoco_version>