Fix STU3 validation: find correct extension profile, use custom datatype profile, allow to disable terminology checks

This commit is contained in:
Sebastien Riviere 2017-07-10 12:38:33 +02:00
parent 6c47bd4c51
commit a8e248036e
4 changed files with 120 additions and 6 deletions

View File

@ -2,6 +2,7 @@ package org.hl7.fhir.dstu3.elementmodel;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.dstu3.conformance.ProfileUtilities;
import org.hl7.fhir.dstu3.context.IWorkerContext;
@ -203,7 +204,7 @@ public class Property {
ElementDefinition ed = definition;
StructureDefinition sd = structure;
List<ElementDefinition> children = ProfileUtilities.getChildMap(sd, ed);
if (children.isEmpty()) {
if (children.isEmpty() || isElementWithOnlyExtension(ed, children)) {
// ok, find the right definitions
String t = null;
if (ed.getType().size() == 1)
@ -240,7 +241,13 @@ public class Property {
}
}
if (!"xhtml".equals(t)) {
sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+t);
final String url;
if (StringUtils.isNotBlank(ed.getType().get(0).getProfile())) {
url = ed.getType().get(0).getProfile();
} else {
url = "http://hl7.org/fhir/StructureDefinition/" + t;
}
sd = context.fetchResource(StructureDefinition.class, url);
if (sd == null)
throw new DefinitionException("Unable to find type '"+t+"' for name '"+elementName+"' on property "+definition.getPath());
children = ProfileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0));
@ -340,5 +347,18 @@ public class Property {
return context;
}
private boolean isElementWithOnlyExtension(final ElementDefinition ed, final List<ElementDefinition> children) {
boolean result = false;
if (!ed.getType().isEmpty()) {
result = true;
for (final ElementDefinition ele : children) {
if (!ele.getPath().contains("extension")) {
result = false;
break;
}
}
}
return result;
}
}

View File

@ -23,6 +23,7 @@ import org.hl7.fhir.dstu3.elementmodel.Element.SpecialElement;
import org.hl7.fhir.dstu3.formats.FormatUtilities;
import org.hl7.fhir.dstu3.formats.IParser.OutputStyle;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.ElementDefinition;
import org.hl7.fhir.dstu3.model.ElementDefinition.PropertyRepresentation;
import org.hl7.fhir.dstu3.model.Enumeration;
import org.hl7.fhir.dstu3.model.StructureDefinition;
@ -45,6 +46,7 @@ import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.apache.commons.lang3.StringUtils;
public class XmlParser extends ParserBase {
private boolean allowXsiLocation;
@ -250,7 +252,13 @@ public class XmlParser extends ParserBase {
Node child = node.getFirstChild();
while (child != null) {
if (child.getNodeType() == Node.ELEMENT_NODE) {
Property property = getElementProp(properties, child.getLocalName());
final Property property;
if (StringUtils.contains(child.getLocalName(), "extension")) {
property = getExtensionProp(properties, child);
} else {
property = getElementProp(properties, child.getLocalName());
}
if (property != null) {
if (!property.isChoice() && "xhtml".equals(property.getType())) {
XhtmlNode xhtml = new XhtmlParser().setValidatorMode(true).parseHtmlNode((org.w3c.dom.Element) child);
@ -293,6 +301,48 @@ public class XmlParser extends ParserBase {
}
}
private Property getExtensionProp(final List<Property> properties, final Node child) {
// get the correct property corresponding to the extension
Property property = null;
final Node extensionNode = child.getAttributes().getNamedItem("url");
if (extensionNode != null) {
for (final Property prop : properties) {
if (prop.getName().contains("extension")) {
if (findExtension(prop.getDefinition(), extensionNode.getNodeValue())) {
property = prop;
break;
}
}
}
}
if (property == null) {
property = getElementProp(properties, child.getLocalName());
}
return isDefaultExtensionElement(property) ? null : property;
}
private boolean isDefaultExtensionElement(final Property property) {
if (property == null) {
return false;
}
final ElementDefinition ed = property.getDefinition();
return StringUtils.contains(ed.getPath(), "extension") && ed.getType().isEmpty() && !ed.getSlicing().isEmpty();
}
private boolean findExtension(final ElementDefinition definition, final String localName) {
boolean result = false;
if(StringUtils.isNotBlank(localName)) {
for (ElementDefinition.TypeRefComponent type:definition.getType()) {
if(localName.equals(type.getProfile())) {
result = true;
break;
}
}
}
return result;
}
private Property getElementProp(List<Property> properties, String nodeName) {
List<Property> propsSortedByLongestFirst = new ArrayList<Property>(properties);
// sort properties according to their name longest first, so .requestOrganizationReference comes first before .request[x]
@ -337,15 +387,34 @@ public class XmlParser extends ParserBase {
private void parseResource(String string, org.w3c.dom.Element container, Element parent, Property elementProperty) throws FHIRFormatError, DefinitionException, FHIRException, IOException {
org.w3c.dom.Element res = XMLUtil.getFirstChild(container);
String name = res.getLocalName();
StructureDefinition sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+name);
final String profile = findProfile(res);
final StructureDefinition sd = context.fetchResource(StructureDefinition.class, profile);
if (sd == null)
throw new FHIRFormatError("Contained resource does not appear to be a FHIR resource (unknown name '"+res.getLocalName()+"')");
parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd), SpecialElement.fromProperty(parent.getProperty()), elementProperty);
parent.setType(name);
parent.setType(res.getLocalName());
parseChildren(res.getLocalName(), res, parent);
}
private String findProfile(final org.w3c.dom.Element res) {
String url = null;
for (int i = 0; i < res.getChildNodes().getLength(); i++) {
final Node child = res.getChildNodes().item(i);
if ("meta".equals(child.getLocalName())) {
for (int j = 0; j < child.getChildNodes().getLength(); j++) {
final Node subChild = child.getChildNodes().item(j);
if ("profile".equals(subChild.getLocalName())) {
final Node profileNode = subChild.getAttributes().getNamedItem("value");
url = profileNode.getNodeValue();
break;
}
}
break;
}
}
return StringUtils.isNotBlank(url) ? url : "http://hl7.org/fhir/StructureDefinition/" + res.getLocalName();
}
private void reapComments(org.w3c.dom.Element element, Element context) {
Node node = element.getPreviousSibling();
while (node != null && node.getNodeType() != Node.ELEMENT_NODE) {

View File

@ -44,6 +44,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
private DocumentBuilderFactory myDocBuilderFactory;
private StructureDefinition myStructureDefintion;
private IValidationSupport myValidationSupport;
private boolean noTerminologyChecks = false;
/**
* Constructor
@ -127,6 +128,21 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
myAnyExtensionsAllowed = theAnyExtensionsAllowed;
}
/**
* If set to {@literal true} (default is false) the valueSet will not be validate
*/
public boolean isNoTerminologyChecks() {
return noTerminologyChecks;
}
/**
* If set to {@literal true} (default is false) the valueSet will not be validate
*/
public void setNoTerminologyChecks(final boolean theNoTerminologyChecks) {
noTerminologyChecks = theNoTerminologyChecks;
}
/**
* Sets the "best practice warning level". When validating, any deviations from best practices will be reported at
* this level.
@ -171,6 +187,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
v.setBestPracticeWarningLevel(getBestPracticeWarningLevel());
v.setAnyExtensionsAllowed(isAnyExtensionsAllowed());
v.setResourceIdRule(IdStatus.OPTIONAL);
v.setNoTerminologyChecks(isNoTerminologyChecks());
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();

View File

@ -7,6 +7,7 @@ import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -838,6 +839,13 @@ public class FhirInstanceValidatorDstu3Test {
assertEquals(0, all.size());
}
@Test
public void testIsNoTerminologyChecks() {
assertFalse(myInstanceVal.isNoTerminologyChecks());
myInstanceVal.setNoTerminologyChecks(true);
assertTrue(myInstanceVal.isNoTerminologyChecks());
}
@Test
@Ignore
public void testValidateStructureDefinition() throws IOException {