Merge branch 'master' of https://github.com/hapifhir/org.hl7.fhir.core
This commit is contained in:
commit
0250f3ce34
|
@ -473,10 +473,12 @@ public class ValidationService {
|
|||
if (sessionId != null) {
|
||||
System.out.println("No such cached session exists for session id " + sessionId + ", re-instantiating validator.");
|
||||
}
|
||||
System.out.println("Building new validator engine from CliContext");
|
||||
ValidationEngine validator = buildValidationEngine(cliContext, definitions, tt);
|
||||
sessionId = sessionCache.cacheSession(validator);
|
||||
System.out.println("Cached new session. Cache size = " + sessionCache.getSessionIds().size());
|
||||
} else {
|
||||
System.out.println("Cached session exists for session id " + sessionId + ", returning stored validator session id.");
|
||||
System.out.println("Cached session exists for session id " + sessionId + ", returning stored validator session id. Cache size = " + sessionCache.getSessionIds().size());
|
||||
}
|
||||
return sessionId;
|
||||
}
|
||||
|
|
|
@ -1029,7 +1029,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
validateResource(new ValidationContext(appContext, element), errors, element, element, defn, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.ConfigProfile), false, false);
|
||||
}
|
||||
}
|
||||
if (hintAboutNonMustSupport) {
|
||||
if (hintAboutNonMustSupport && !profiles.isEmpty()) {
|
||||
checkElementUsage(errors, element, stack);
|
||||
}
|
||||
codingObserver.finish(errors, stack);
|
||||
|
@ -1042,10 +1042,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
|
||||
|
||||
private void checkElementUsage(List<ValidationMessage> errors, Element element, NodeStack stack) {
|
||||
String elementUsage = element.getUserString("elementSupported");
|
||||
hint(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), elementUsage == null || elementUsage.equals("Y"), I18nConstants.MUSTSUPPORT_VAL_MUSTSUPPORT, element.getName(), element.getProperty().getStructure().getVersionedUrl());
|
||||
if (element.getPath()==null
|
||||
|| (element.getName().equals("id") && !element.getPath().substring(0, element.getPath().length()-3).contains("."))
|
||||
|| (element.getName().equals("text") && !element.getPath().substring(0, element.getPath().length()-5).contains(".")))
|
||||
return;
|
||||
String hasFixed = element.getUserString("hasFixed");
|
||||
if (element.getPath().contains(".") && (hasFixed== null || !hasFixed.equals("Y"))) {
|
||||
String elementUsage = element.getUserString("elementSupported");
|
||||
hint(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), elementUsage != null && (elementUsage.equals("Y") || elementUsage.equals("NA")), I18nConstants.MUSTSUPPORT_VAL_MUSTSUPPORT, element.getName(), element.getProperty().getStructure().getVersionedUrl());
|
||||
if (elementUsage==null || !elementUsage.equals("Y"))
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.hasChildren()) {
|
||||
if (element.hasChildren() && (hasFixed== null || !hasFixed.equals("Y"))) {
|
||||
String prevName = "";
|
||||
int elementCount = 0;
|
||||
for (Element ce : element.getChildren()) {
|
||||
|
@ -6745,13 +6754,16 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
profile.setUserData("usesMustSupport", usesMustSupport);
|
||||
}
|
||||
if (usesMustSupport.equals("Y")) {
|
||||
String elementSupported = ei.getElement().getUserString("elementSupported");
|
||||
if (elementSupported == null || ei.definition.getMustSupport())
|
||||
if (ei.definition.getMustSupport()) {
|
||||
ei.getElement().setUserData("elementSupported", "Y");
|
||||
}
|
||||
}
|
||||
String elementSupported = ei.getElement().getUserString("elementSupported");
|
||||
String fixedValue = ei.getElement().getUserString("hasFixed");
|
||||
if ((elementSupported == null || !elementSupported.equals("Y")) && ei.definition.getMustSupport()) {
|
||||
if (ei.definition.getMustSupport()) {
|
||||
ei.getElement().setUserData("elementSupported", "Y");
|
||||
}
|
||||
} else if (elementSupported == null && !usesMustSupport.equals("Y"))
|
||||
ei.getElement().setUserData("elementSupported", "NA");
|
||||
if (fixedValue==null && (ei.definition.hasFixed() || ei.definition.hasPattern()))
|
||||
ei.getElement().setUserData("hasFixed", "Y");
|
||||
}
|
||||
|
||||
public boolean checkCardinalities(List<ValidationMessage> errors, StructureDefinition profile, Element element, NodeStack stack,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
package org.hl7.fhir.validation.profile;
|
||||
|
||||
package org.hl7.fhir.validation.profile;
|
||||
|
||||
/*
|
||||
Copyright (c) 2011+, HL7, Inc.
|
||||
All rights reserved.
|
||||
|
@ -28,138 +28,140 @@ package org.hl7.fhir.validation.profile;
|
|||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.fhirpath.FHIRPathEngine;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
|
||||
import org.hl7.fhir.r5.utils.XVerExtensionManager;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||
import org.hl7.fhir.validation.BaseValidator;
|
||||
|
||||
public class ProfileValidator extends BaseValidator {
|
||||
|
||||
private boolean checkAggregation = false;
|
||||
private boolean checkMustSupport = false;
|
||||
private boolean allowDoubleQuotesInFHIRPath = false;
|
||||
private FHIRPathEngine fpe;
|
||||
|
||||
public ProfileValidator(IWorkerContext context, XVerExtensionManager xverManager) {
|
||||
super(context, xverManager, false);
|
||||
fpe = new FHIRPathEngine(context);
|
||||
fpe.setAllowDoubleQuotes(allowDoubleQuotesInFHIRPath);
|
||||
}
|
||||
|
||||
public boolean isCheckAggregation() {
|
||||
return checkAggregation;
|
||||
}
|
||||
|
||||
public boolean isCheckMustSupport() {
|
||||
return checkMustSupport;
|
||||
}
|
||||
|
||||
public void setCheckAggregation(boolean checkAggregation) {
|
||||
this.checkAggregation = checkAggregation;
|
||||
}
|
||||
|
||||
public void setCheckMustSupport(boolean checkMustSupport) {
|
||||
this.checkMustSupport = checkMustSupport;
|
||||
}
|
||||
|
||||
public boolean isAllowDoubleQuotesInFHIRPath() {
|
||||
return allowDoubleQuotesInFHIRPath;
|
||||
}
|
||||
|
||||
public void setAllowDoubleQuotesInFHIRPath(boolean allowDoubleQuotesInFHIRPath) {
|
||||
this.allowDoubleQuotesInFHIRPath = allowDoubleQuotesInFHIRPath;
|
||||
}
|
||||
|
||||
protected boolean rule(List<ValidationMessage> errors, IssueType type, String path, boolean b, String msg) {
|
||||
String rn = path.contains(".") ? path.substring(0, path.indexOf(".")) : path;
|
||||
return super.ruleHtml(errors, NO_RULE_DATE, type, path, b, msg, "<a href=\""+(rn.toLowerCase())+".html\">"+rn+"</a>: "+Utilities.escapeXml(msg));
|
||||
}
|
||||
|
||||
public List<ValidationMessage> validate(StructureDefinition profile, boolean forBuild) {
|
||||
List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
|
||||
// must have a FHIR version- GF#3160
|
||||
String s = (profile.getKind() == StructureDefinitionKind.LOGICAL) ? "Logical Models" : "Profiles";
|
||||
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, profile.getUrl(), profile.hasFhirVersion(), s+" SHOULD state the FHIR Version on which they are based");
|
||||
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, profile.getUrl(), profile.hasVersion(), s+" SHOULD state their own version");
|
||||
|
||||
// extensions must be defined
|
||||
for (ElementDefinition ec : profile.getDifferential().getElement())
|
||||
checkExtensions(profile, errors, "differential", ec);
|
||||
rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, profile.getId(), profile.hasSnapshot(), "missing Snapshot at "+profile.getName()+"."+profile.getName());
|
||||
for (ElementDefinition ec : profile.getSnapshot().getElement())
|
||||
checkExtensions(profile, errors, "snapshot", ec);
|
||||
|
||||
if (rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, profile.getId(), profile.hasSnapshot(), "A snapshot is required")) {
|
||||
Hashtable<String, ElementDefinition> snapshotElements = new Hashtable<String, ElementDefinition>();
|
||||
for (ElementDefinition ed : profile.getSnapshot().getElement()) {
|
||||
snapshotElements.put(ed.getId(), ed);
|
||||
for (ElementDefinitionConstraintComponent inv : ed.getConstraint()) {
|
||||
if (forBuild) {
|
||||
if (!inExemptList(inv.getKey())) {
|
||||
// if (rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, profile.getId()+"::"+ed.getPath()+"::"+inv.getKey(), inv.hasExpression(), "The invariant has no FHIR Path expression ("+inv.getXpath()+")")) {
|
||||
// try {
|
||||
// fpe.check(null, profile.getType(), ed.getPath(), inv.getExpression()); // , inv.hasXpath() && inv.getXpath().startsWith("@value")
|
||||
// } catch (Exception e) {
|
||||
// // rule(errors, UNKNOWN_DATE_TIME, IssueType.STRUCTURE, profile.getId()+"::"+ed.getPath()+"::"+inv.getId(), false, e.getMessage());
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (snapshotElements != null) {
|
||||
for (ElementDefinition diffElement : profile.getDifferential().getElement()) {
|
||||
if (diffElement == null)
|
||||
throw new Error("Diff Element is null - this is not an expected thing");
|
||||
ElementDefinition snapElement = snapshotElements.get(diffElement.getId());
|
||||
if (snapElement!=null) { // Happens with profiles in the main build - should be able to fix once snapshot generation is fixed - Lloyd
|
||||
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, diffElement.getId(), !checkMustSupport || snapElement.hasMustSupport(), "Elements included in the differential should declare mustSupport");
|
||||
if (checkAggregation) {
|
||||
for (TypeRefComponent type : snapElement.getType()) {
|
||||
if ("http://hl7.org/fhir/Reference".equals(type.getWorkingCode()) || "http://hl7.org/fhir/canonical".equals(type.getWorkingCode())) {
|
||||
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, diffElement.getId(), type.hasAggregation(), "Elements with type Reference or canonical should declare aggregation");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
// these are special cases
|
||||
private boolean inExemptList(String key) {
|
||||
return key.startsWith("txt-");
|
||||
}
|
||||
|
||||
private boolean checkExtensions(StructureDefinition profile, List<ValidationMessage> errors, String kind, ElementDefinition ec) {
|
||||
if (!ec.getType().isEmpty() && "Extension".equals(ec.getType().get(0).getWorkingCode()) && ec.getType().get(0).hasProfile()) {
|
||||
String url = ec.getType().get(0).getProfile().get(0).getValue();
|
||||
StructureDefinition defn = context.fetchResource(StructureDefinition.class, url);
|
||||
if (defn == null) {
|
||||
defn = getXverExt(profile, errors, url);
|
||||
}
|
||||
return rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, profile.getId(), defn != null, "Unable to find Extension '"+url+"' referenced at "+profile.getUrl()+" "+kind+" "+ec.getPath()+" ("+ec.getSliceName()+")");
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.fhirpath.FHIRPathEngine;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
|
||||
import org.hl7.fhir.r5.utils.XVerExtensionManager;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||
import org.hl7.fhir.validation.BaseValidator;
|
||||
|
||||
public class ProfileValidator extends BaseValidator {
|
||||
|
||||
private boolean checkAggregation = false;
|
||||
private boolean checkMustSupport = false;
|
||||
private boolean allowDoubleQuotesInFHIRPath = false;
|
||||
private FHIRPathEngine fpe;
|
||||
|
||||
public ProfileValidator(IWorkerContext context, XVerExtensionManager xverManager) {
|
||||
super(context, xverManager, false);
|
||||
fpe = new FHIRPathEngine(context);
|
||||
fpe.setAllowDoubleQuotes(allowDoubleQuotesInFHIRPath);
|
||||
}
|
||||
|
||||
public boolean isCheckAggregation() {
|
||||
return checkAggregation;
|
||||
}
|
||||
|
||||
public boolean isCheckMustSupport() {
|
||||
return checkMustSupport;
|
||||
}
|
||||
|
||||
public void setCheckAggregation(boolean checkAggregation) {
|
||||
this.checkAggregation = checkAggregation;
|
||||
}
|
||||
|
||||
public void setCheckMustSupport(boolean checkMustSupport) {
|
||||
this.checkMustSupport = checkMustSupport;
|
||||
}
|
||||
|
||||
public boolean isAllowDoubleQuotesInFHIRPath() {
|
||||
return allowDoubleQuotesInFHIRPath;
|
||||
}
|
||||
|
||||
public void setAllowDoubleQuotesInFHIRPath(boolean allowDoubleQuotesInFHIRPath) {
|
||||
this.allowDoubleQuotesInFHIRPath = allowDoubleQuotesInFHIRPath;
|
||||
}
|
||||
|
||||
protected boolean rule(List<ValidationMessage> errors, IssueType type, String path, boolean b, String msg) {
|
||||
String rn = path.contains(".") ? path.substring(0, path.indexOf(".")) : path;
|
||||
return super.ruleHtml(errors, NO_RULE_DATE, type, path, b, msg, "<a href=\""+(rn.toLowerCase())+".html\">"+rn+"</a>: "+Utilities.escapeXml(msg));
|
||||
}
|
||||
|
||||
public List<ValidationMessage> validate(StructureDefinition profile, boolean forBuild) {
|
||||
List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
|
||||
// must have a FHIR version- GF#3160
|
||||
String s = (profile.getKind() == StructureDefinitionKind.LOGICAL) ? "Logical Models" : "Profiles";
|
||||
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, profile.getUrl(), profile.hasFhirVersion(), s+" SHOULD state the FHIR Version on which they are based");
|
||||
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, profile.getUrl(), profile.hasVersion(), s+" SHOULD state their own version");
|
||||
|
||||
// extensions must be defined
|
||||
for (ElementDefinition ec : profile.getDifferential().getElement())
|
||||
checkExtensions(profile, errors, "differential", ec);
|
||||
rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, profile.getId(), profile.hasSnapshot(), "missing Snapshot at "+profile.getName()+"."+profile.getName());
|
||||
for (ElementDefinition ec : profile.getSnapshot().getElement())
|
||||
checkExtensions(profile, errors, "snapshot", ec);
|
||||
|
||||
if (rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, profile.getId(), profile.hasSnapshot(), "A snapshot is required")) {
|
||||
Hashtable<String, ElementDefinition> snapshotElements = new Hashtable<String, ElementDefinition>();
|
||||
for (ElementDefinition ed : profile.getSnapshot().getElement()) {
|
||||
snapshotElements.put(ed.getId(), ed);
|
||||
for (ElementDefinitionConstraintComponent inv : ed.getConstraint()) {
|
||||
if (forBuild) {
|
||||
if (!inExemptList(inv.getKey())) {
|
||||
// if (rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, profile.getId()+"::"+ed.getPath()+"::"+inv.getKey(), inv.hasExpression(), "The invariant has no FHIR Path expression ("+inv.getXpath()+")")) {
|
||||
// try {
|
||||
// fpe.check(null, profile.getType(), ed.getPath(), inv.getExpression()); // , inv.hasXpath() && inv.getXpath().startsWith("@value")
|
||||
// } catch (Exception e) {
|
||||
// // rule(errors, UNKNOWN_DATE_TIME, IssueType.STRUCTURE, profile.getId()+"::"+ed.getPath()+"::"+inv.getId(), false, e.getMessage());
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (snapshotElements != null) {
|
||||
for (ElementDefinition diffElement : profile.getDifferential().getElement()) {
|
||||
if (diffElement == null)
|
||||
throw new Error("Diff Element is null - this is not an expected thing");
|
||||
ElementDefinition snapElement = snapshotElements.get(diffElement.getId());
|
||||
if (snapElement!=null) { // Happens with profiles in the main build - should be able to fix once snapshot generation is fixed - Lloyd
|
||||
// We don't care about mustSupport for the root element, if the element is just declaring slicing, if there's a pattern or fixed value, or if the element is being prohibited.
|
||||
boolean noMustSupport = !checkMustSupport || !snapElement.getPath().contains(".") || snapElement.hasSlicing() || snapElement.hasPattern() || snapElement.hasFixed() || snapElement.getMax().equals("0") || snapElement.hasMustSupport();
|
||||
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, diffElement.getId(), noMustSupport, "Elements included in the differential that aren't prohibited and don't have fixed values or patterns should declare mustSupport: " + snapElement.getPath());
|
||||
if (checkAggregation) {
|
||||
for (TypeRefComponent type : snapElement.getType()) {
|
||||
if ("http://hl7.org/fhir/Reference".equals(type.getWorkingCode()) || "http://hl7.org/fhir/canonical".equals(type.getWorkingCode())) {
|
||||
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, diffElement.getId(), type.hasAggregation(), "Elements with type Reference or canonical should declare aggregation");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
// these are special cases
|
||||
private boolean inExemptList(String key) {
|
||||
return key.startsWith("txt-");
|
||||
}
|
||||
|
||||
private boolean checkExtensions(StructureDefinition profile, List<ValidationMessage> errors, String kind, ElementDefinition ec) {
|
||||
if (!ec.getType().isEmpty() && "Extension".equals(ec.getType().get(0).getWorkingCode()) && ec.getType().get(0).hasProfile()) {
|
||||
String url = ec.getType().get(0).getProfile().get(0).getValue();
|
||||
StructureDefinition defn = context.fetchResource(StructureDefinition.class, url);
|
||||
if (defn == null) {
|
||||
defn = getXverExt(profile, errors, url);
|
||||
}
|
||||
return rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, profile.getId(), defn != null, "Unable to find Extension '"+url+"' referenced at "+profile.getUrl()+" "+kind+" "+ec.getPath()+" ("+ec.getSliceName()+")");
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
10
pom.xml
10
pom.xml
|
@ -741,6 +741,16 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<phase>deploy</phase>
|
||||
<goals><goal>jar</goal></goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
|
Loading…
Reference in New Issue