Merge branch 'master' into Snapshot-issue

This commit is contained in:
Lloyd McKenzie 2019-02-06 23:25:13 -07:00 committed by GitHub
commit a17754792b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 4966 additions and 4314 deletions

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId> <artifactId>org.hl7.fhir.core</artifactId>
<version>3.7.3-SNAPSHOT</version> <version>3.7.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -33,6 +33,7 @@ import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.Questionnaire;
import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Enumeration; import org.hl7.fhir.r4.model.Enumeration;
import org.hl7.fhir.r4.model.Expression.ExpressionLanguage; import org.hl7.fhir.r4.model.Expression.ExpressionLanguage;
@ -76,7 +77,7 @@ import org.hl7.fhir.utilities.Utilities;
public class VersionConvertor_30_40 { public class VersionConvertor_30_40 {
private static List<String> CANONICAL_URLS = new ArrayList<String>(); private static List<String> CANONICAL_URLS = new ArrayList<>();
static { static {
CANONICAL_URLS.add("http://hl7.org/fhir/StructureDefinition/11179-permitted-value-conceptmap"); CANONICAL_URLS.add("http://hl7.org/fhir/StructureDefinition/11179-permitted-value-conceptmap");
CANONICAL_URLS.add("http://hl7.org/fhir/StructureDefinition/11179-permitted-value-valueset"); CANONICAL_URLS.add("http://hl7.org/fhir/StructureDefinition/11179-permitted-value-valueset");
@ -16013,6 +16014,7 @@ public class VersionConvertor_30_40 {
tgt.setType(convertQuestionnaireItemType(src.getType())); tgt.setType(convertQuestionnaireItemType(src.getType()));
for (org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemEnableWhenComponent t : src.getEnableWhen()) for (org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemEnableWhenComponent t : src.getEnableWhen())
tgt.addEnableWhen(convertQuestionnaireItemEnableWhenComponent(t)); tgt.addEnableWhen(convertQuestionnaireItemEnableWhenComponent(t));
tgt.setEnableBehavior(Questionnaire.EnableWhenBehavior.ANY);
if (src.hasRequired()) if (src.hasRequired())
tgt.setRequired(src.getRequired()); tgt.setRequired(src.getRequired());
if (src.hasRepeats()) if (src.hasRepeats())
@ -16029,6 +16031,9 @@ public class VersionConvertor_30_40 {
tgt.addInitial().setValue(convertType(src.getInitial())); tgt.addInitial().setValue(convertType(src.getInitial()));
for (org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent t : src.getItem()) for (org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent t : src.getItem())
tgt.addItem(convertQuestionnaireItemComponent(t)); tgt.addItem(convertQuestionnaireItemComponent(t));
for (org.hl7.fhir.dstu3.model.Extension t : src.getModifierExtension()) {
tgt.addModifierExtension(convertExtension(t));
}
return tgt; return tgt;
} }
@ -16131,8 +16136,10 @@ public class VersionConvertor_30_40 {
tgt.setOperator(org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemOperator.EXISTS); tgt.setOperator(org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemOperator.EXISTS);
tgt.setAnswer(convertType(src.getHasAnswerElement())); tgt.setAnswer(convertType(src.getHasAnswerElement()));
} }
else if (src.hasAnswer()) else if (src.hasAnswer()) {
tgt.setOperator(org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemOperator.EQUAL);
tgt.setAnswer(convertType(src.getAnswer())); tgt.setAnswer(convertType(src.getAnswer()));
}
return tgt; return tgt;
} }

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId> <artifactId>org.hl7.fhir.core</artifactId>
<version>3.7.3-SNAPSHOT</version> <version>3.7.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId> <artifactId>org.hl7.fhir.core</artifactId>
<version>3.7.3-SNAPSHOT</version> <version>3.7.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId> <artifactId>org.hl7.fhir.core</artifactId>
<version>3.7.3-SNAPSHOT</version> <version>3.7.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId> <artifactId>org.hl7.fhir.core</artifactId>
<version>3.7.3-SNAPSHOT</version> <version>3.7.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -650,10 +650,16 @@ public class ProfileUtilities extends TranslatingUtilities {
} }
if (outcome.getType().size() > 1) if (outcome.getType().size() > 1)
for (TypeRefComponent t : outcome.getType()) { for (TypeRefComponent t : outcome.getType()) {
if (!t.getCode().equals("Reference")) if (!t.getCode().equals("Reference")) {
boolean nonExtension = false;
for (ElementDefinition ed : diffMatches)
if (ed != diffMatches.get(0) && !ed.getPath().endsWith(".extension"))
nonExtension = true;
if (nonExtension)
throw new DefinitionException(diffMatches.get(0).getPath()+" has children ("+differential.getElement().get(diffCursor).getPath()+") and multiple types ("+typeCode(outcome.getType())+") in profile "+profileName); throw new DefinitionException(diffMatches.get(0).getPath()+" has children ("+differential.getElement().get(diffCursor).getPath()+") and multiple types ("+typeCode(outcome.getType())+") in profile "+profileName);
} }
} }
}
int start = diffCursor; int start = diffCursor;
while (differential.getElement().size() > diffCursor && pathStartsWith(differential.getElement().get(diffCursor).getPath(), diffMatches.get(0).getPath()+".")) while (differential.getElement().size() > diffCursor && pathStartsWith(differential.getElement().get(diffCursor).getPath(), diffMatches.get(0).getPath()+"."))
diffCursor++; diffCursor++;
@ -668,7 +674,7 @@ public class ProfileUtilities extends TranslatingUtilities {
nbl++; nbl++;
processPaths(indent+" ", result, base, differential, nbc, start - 1, nbl-1, diffCursor - 1, url, profileName, tgt.getPath(), diffMatches.get(0).getPath(), trimDifferential, contextName, resultPathBase, false, redirectorStack(redirector, outcome, cpath)); processPaths(indent+" ", result, base, differential, nbc, start - 1, nbl-1, diffCursor - 1, url, profileName, tgt.getPath(), diffMatches.get(0).getPath(), trimDifferential, contextName, resultPathBase, false, redirectorStack(redirector, outcome, cpath));
} else { } else {
StructureDefinition dt = getProfileForDataType(outcome.getType().get(0)); StructureDefinition dt = outcome.getType().size() == 1 ? getProfileForDataType(outcome.getType().get(0)) : getProfileForDataType("Element");
if (dt == null) if (dt == null)
throw new DefinitionException(diffMatches.get(0).getPath()+" has children ("+differential.getElement().get(diffCursor).getPath()+") for type "+typeCode(outcome.getType())+" in profile "+profileName+", but can't find type"); throw new DefinitionException(diffMatches.get(0).getPath()+" has children ("+differential.getElement().get(diffCursor).getPath()+") for type "+typeCode(outcome.getType())+" in profile "+profileName+", but can't find type");
contextName = dt.getUrl(); contextName = dt.getUrl();
@ -1194,6 +1200,13 @@ public class ProfileUtilities extends TranslatingUtilities {
return sd; return sd;
} }
private StructureDefinition getProfileForDataType(String type) {
StructureDefinition sd = context.fetchTypeDefinition(type);
if (sd == null)
System.out.println("XX: failed to find profle for type: " + type); // debug GJM
return sd;
}
public static String typeCode(List<TypeRefComponent> types) { public static String typeCode(List<TypeRefComponent> types) {
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();

View File

@ -153,6 +153,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
public static SimpleWorkerContext fromPackage(NpmPackage pi, IContextResourceLoader loader) throws FileNotFoundException, IOException, FHIRException { public static SimpleWorkerContext fromPackage(NpmPackage pi, IContextResourceLoader loader) throws FileNotFoundException, IOException, FHIRException {
SimpleWorkerContext res = new SimpleWorkerContext(); SimpleWorkerContext res = new SimpleWorkerContext();
res.setAllowLoadingDuplicates(true); res.setAllowLoadingDuplicates(true);
res.version = pi.getNpm().get("version").getAsString();
res.loadFromPackage(pi, loader); res.loadFromPackage(pi, loader);
return res; return res;
} }
@ -523,7 +524,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
@Override @Override
public String getVersion() { public String getVersion() {
return version+"-"+revision; return version;
} }

View File

@ -512,12 +512,12 @@ public class Element extends Base {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<ElementDecoration> decorations = (List<ElementDecoration>) getUserData("fhir.decorations"); List<ElementDecoration> decorations = (List<ElementDecoration>) getUserData("fhir.decorations");
if (decorations == null) { if (decorations == null) {
decorations = new ArrayList<ElementDecoration>(); decorations = new ArrayList<>();
setUserData("fhir.decorations", decorations); setUserData("fhir.decorations", decorations);
} }
decorations.add(new ElementDecoration(DecorationType.TYPE, profile.getUserString("path"), definition.getPath())); decorations.add(new ElementDecoration(DecorationType.TYPE, profile.getUserString("path"), definition.getPath()));
if (tail(definition.getId()).contains(":")) { if (definition.getId() != null && tail(definition.getId()).contains(":")) {
String[] details = tail(definition.getId()).split("\\:"); String[] details = tail(definition.getId()).split(":");
decorations.add(new ElementDecoration(DecorationType.SLICE, null, details[1])); decorations.add(new ElementDecoration(DecorationType.SLICE, null, details[1]));
} }
} }

View File

@ -1906,8 +1906,10 @@ public class FHIRPathEngine {
} }
private List<Base> opIn(List<Base> left, List<Base> right) throws FHIRException { private List<Base> opIn(List<Base> left, List<Base> right) throws FHIRException {
if (left.size() == 0 || right.size() == 0) if (left.size() == 0)
return new ArrayList<Base>(); return new ArrayList<Base>();
if (right.size() == 0)
return makeBoolean(false);
boolean ans = true; boolean ans = true;
for (Base l : left) { for (Base l : left) {
boolean f = false; boolean f = false;

View File

@ -39,7 +39,7 @@
34. Referencing a custom extension 34. Referencing a custom extension
35. Referencing a profiled custom extension 35. Referencing a profiled custom extension
36. Deep recursion 36. Deep recursion
37. Constrained polymorphic elements 37. Test Bad Path
38. Deep nesting 38. Deep nesting
--> -->
<contained> <contained>
@ -1832,10 +1832,10 @@
<contained> <contained>
<StructureDefinition> <StructureDefinition>
<id value="t37"/> <id value="t37"/>
<url value="urn:uuid:2918bbbf-2145-4c13-822e-7dfd758694ec"/> <url value="urn:uuid:44a92f3e-3681-4dbc-baa2-48a07e2765ca"/>
<name value="t38"/> <name value="t37"/>
<status value="draft"/> <status value="draft"/>
<description value="fixture for #38: Constrained polymporphic elements"/> <description value="fixture for #37: Test bad Path"/>
<kind value="resource"/> <kind value="resource"/>
<abstract value="false"/> <abstract value="false"/>
<type value="MedicationRequest"/> <type value="MedicationRequest"/>
@ -1992,6 +1992,13 @@
<sourceId value="t1"/> <sourceId value="t1"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test1-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test1-1"/> <label value="test1-1"/>
@ -2053,6 +2060,13 @@
<sourceId value="t2"/> <sourceId value="t2"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test2-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test2-1"/> <label value="test2-1"/>
@ -2087,6 +2101,13 @@
<sourceId value="t3"/> <sourceId value="t3"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test3-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test3-1"/> <label value="test3-1"/>
@ -2130,6 +2151,13 @@
<sourceId value="t4"/> <sourceId value="t4"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test4-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test4-1"/> <label value="test4-1"/>
@ -2155,6 +2183,13 @@
<sourceId value="t5"/> <sourceId value="t5"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test5-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test5-1"/> <label value="test5-1"/>
@ -2189,6 +2224,13 @@
<sourceId value="t6"/> <sourceId value="t6"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test6-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test6-1"/> <label value="test6-1"/>
@ -2223,6 +2265,13 @@
<sourceId value="t7"/> <sourceId value="t7"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test7-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test7-1"/> <label value="test7-1"/>
@ -2248,6 +2297,13 @@
<sourceId value="t8"/> <sourceId value="t8"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test8-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test8-1"/> <label value="test8-1"/>
@ -2282,6 +2338,13 @@
<sourceId value="t9"/> <sourceId value="t9"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test9-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test9-1"/> <label value="test9-1"/>
@ -2316,6 +2379,13 @@
<sourceId value="t10"/> <sourceId value="t10"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test10-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test10-1"/> <label value="test10-1"/>
@ -2386,6 +2456,13 @@
<sourceId value="t11"/> <sourceId value="t11"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test11-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test11-1"/> <label value="test11-1"/>
@ -2465,6 +2542,13 @@
<sourceId value="t12"/> <sourceId value="t12"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test12-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test12-1"/> <label value="test12-1"/>
@ -2535,6 +2619,13 @@
<sourceId value="t12a"/> <sourceId value="t12a"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test12a-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test12a-1"/> <label value="test12a-1"/>
@ -2596,6 +2687,13 @@
<sourceId value="t13"/> <sourceId value="t13"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test13-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test13-1"/> <label value="test13-1"/>
@ -2804,6 +2902,13 @@
<sourceId value="t14"/> <sourceId value="t14"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test14-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test14-1"/> <label value="test14-1"/>
@ -2828,6 +2933,13 @@
<sourceId value="t15"/> <sourceId value="t15"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test15-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test15-1"/> <label value="test15-1"/>
@ -2852,6 +2964,13 @@
<sourceId value="t16"/> <sourceId value="t16"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test16-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test16-1"/> <label value="test16-1"/>
@ -2876,6 +2995,13 @@
<sourceId value="t17"/> <sourceId value="t17"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test17-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test17-1"/> <label value="test17-1"/>
@ -2955,6 +3081,13 @@
<sourceId value="t18"/> <sourceId value="t18"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test18-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test18-1"/> <label value="test18-1"/>
@ -3034,6 +3167,13 @@
<sourceId value="t19"/> <sourceId value="t19"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test19-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test19-1"/> <label value="test19-1"/>
@ -3095,6 +3235,13 @@
<sourceId value="t20"/> <sourceId value="t20"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test20-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test20-1"/> <label value="test20-1"/>
@ -3165,6 +3312,13 @@
<sourceId value="t21"/> <sourceId value="t21"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test21-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test21-1"/> <label value="test21-1"/>
@ -3226,6 +3380,13 @@
<sourceId value="t22"/> <sourceId value="t22"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test22-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test22-1"/> <label value="test22-1"/>
@ -3251,6 +3412,13 @@
<sourceId value="t23"/> <sourceId value="t23"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test23-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test23-1"/> <label value="test23-1"/>
@ -3287,6 +3455,13 @@
<sourceId value="t24b"/> <sourceId value="t24b"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test24-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test24-1"/> <label value="test24-1"/>
@ -3319,6 +3494,13 @@
<sourceId value="t25b"/> <sourceId value="t25b"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test25-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test25-1"/> <label value="test25-1"/>
@ -3344,6 +3526,13 @@
<sourceId value="t26"/> <sourceId value="t26"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test26-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test26-1"/> <label value="test26-1"/>
@ -3368,6 +3557,13 @@
<sourceId value="t27"/> <sourceId value="t27"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test27-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test27-1"/> <label value="test27-1"/>
@ -3403,6 +3599,13 @@
<sourceId value="t28"/> <sourceId value="t28"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test28-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test28-1"/> <label value="test28-1"/>
@ -3429,6 +3632,13 @@
<sourceId value="t29"/> <sourceId value="t29"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test29-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test29-1"/> <label value="test29-1"/>
@ -3466,7 +3676,14 @@
</action> </action>
<action> <action>
<assert> <assert>
<label value="test30"/> <label value="test30-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action>
<assert>
<label value="test30-1"/>
<description value="Snapshot doesn't result in an error'"/> <description value="Snapshot doesn't result in an error'"/>
<direction value="response"/> <direction value="response"/>
<expression value="fixture('t30bo').snapshot.element.count().trace('t30bo')>1"/> <expression value="fixture('t30bo').snapshot.element.count().trace('t30bo')>1"/>
@ -3490,6 +3707,13 @@
<sourceId value="t31"/> <sourceId value="t31"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test31-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test31"/> <label value="test31"/>
@ -3516,6 +3740,13 @@
<sourceId value="t32"/> <sourceId value="t32"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test32-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test32"/> <label value="test32"/>
@ -3542,6 +3773,13 @@
<sourceId value="t33"/> <sourceId value="t33"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test33-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test33"/> <label value="test33"/>
@ -3568,6 +3806,13 @@
<sourceId value="t34"/> <sourceId value="t34"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test34-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test34"/> <label value="test34"/>
@ -3603,6 +3848,13 @@
<sourceId value="t35"/> <sourceId value="t35"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test35-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test35"/> <label value="test35"/>
@ -3638,6 +3890,13 @@
<sourceId value="t36"/> <sourceId value="t36"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test36-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test36"/> <label value="test36"/>
@ -3650,7 +3909,7 @@
</test> </test>
<test> <test>
<name value="Test37"/> <name value="Test37"/>
<description value="Test deep nesting"/> <description value="Test bad path"/>
<action> <action>
<operation> <operation>
<type> <type>
@ -3668,7 +3927,6 @@
<assert> <assert>
<label value="test37-0"/> <label value="test37-0"/>
<description value="Snapshot must not generate ok"/> <description value="Snapshot must not generate ok"/>
<direction value="response"/>
<response value="bad"/> <response value="bad"/>
<warningOnly value="false"/> <warningOnly value="false"/>
</assert> </assert>
@ -3690,6 +3948,13 @@
<sourceId value="t38"/> <sourceId value="t38"/>
</operation> </operation>
</action> </action>
<action>
<assert>
<label value="test38-0"/>
<description value="Snapshot must generate ok"/>
<response value="okay"/>
</assert>
</action>
<action> <action>
<assert> <assert>
<label value="test38"/> <label value="test38"/>

View File

@ -33,6 +33,7 @@ import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind; import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r4.model.TestScript; import org.hl7.fhir.r4.model.TestScript;
import org.hl7.fhir.r4.model.TestScript.AssertionResponseTypes;
import org.hl7.fhir.r4.model.TestScript.SetupActionAssertComponent; import org.hl7.fhir.r4.model.TestScript.SetupActionAssertComponent;
import org.hl7.fhir.r4.model.TestScript.SetupActionOperationComponent; import org.hl7.fhir.r4.model.TestScript.SetupActionOperationComponent;
import org.hl7.fhir.r4.model.TestScript.TestActionComponent; import org.hl7.fhir.r4.model.TestScript.TestActionComponent;
@ -167,8 +168,8 @@ public class SnapShotGenerationTests {
throw new Error("Unsupported: actions must have a label"); throw new Error("Unsupported: actions must have a label");
if (!a.hasDescription()) if (!a.hasDescription())
throw new Error("Unsupported: actions must have a description"); throw new Error("Unsupported: actions must have a description");
if (!a.hasExpression() && !(a.hasResponse() && a.getResponse().equals(TestScript.AssertionResponseTypes.BAD))) if (!a.hasExpression() && !a.hasResponse())
throw new Error("Unsupported: actions must have an expression or a response type of 'bad'"); throw new Error("Unsupported: actions must have an expression or a response");
} else { } else {
throw new Error("Unsupported: Unrecognized action type"); throw new Error("Unsupported: Unrecognized action type");
} }
@ -323,10 +324,12 @@ public class SnapShotGenerationTests {
resolveFixtures(); resolveFixtures();
TestScript.AssertionResponseTypes lastOpOutcome = null;
for (int i = 0; i < test.getAction().size(); i++) { for (int i = 0; i < test.getAction().size(); i++) {
TestActionComponent action = test.getAction().get(i); TestActionComponent action = test.getAction().get(i);
try {
if (action.hasOperation()) { if (action.hasOperation()) {
lastOpOutcome = AssertionResponseTypes.OKAY;
try {
SetupActionOperationComponent op = action.getOperation(); SetupActionOperationComponent op = action.getOperation();
Coding opType = op.getType(); Coding opType = op.getType();
if (opType.getSystem().equals("http://hl7.org/fhir/testscript-operation-codes") && opType.getCode().equals("snapshot")) { if (opType.getSystem().equals("http://hl7.org/fhir/testscript-operation-codes") && opType.getCode().equals("snapshot")) {
@ -350,7 +353,6 @@ public class SnapShotGenerationTests {
if (output.getDifferential().hasElement()) if (output.getDifferential().hasElement())
new NarrativeGenerator("", "http://hl7.org/fhir", TestingUtilities.context()).setPkp(new TestPKP()).generate(output, null); new NarrativeGenerator("", "http://hl7.org/fhir", TestingUtilities.context()).setPkp(new TestPKP()).generate(output, null);
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(System.getProperty("java.io.tmpdir"), op.getResponseId()+"-d.xml")), output); new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(System.getProperty("java.io.tmpdir"), op.getResponseId()+"-d.xml")), output);
} else if (opType.getSystem().equals("http://hl7.org/fhir/testscript-operation-codes") && opType.getCode().equals("sortDifferential")) { } else if (opType.getSystem().equals("http://hl7.org/fhir/testscript-operation-codes") && opType.getCode().equals("sortDifferential")) {
StructureDefinition source = (StructureDefinition) context.fetchFixture(op.getSourceId()); StructureDefinition source = (StructureDefinition) context.fetchFixture(op.getSourceId());
StructureDefinition base = getSD(source.getBaseDefinition()); StructureDefinition base = getSD(source.getBaseDefinition());
@ -364,6 +366,8 @@ public class SnapShotGenerationTests {
context.fixtures.put(op.getResponseId(), output); context.fixtures.put(op.getResponseId(), output);
context.snapshots.put(output.getUrl(), output); context.snapshots.put(output.getUrl(), output);
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(System.getProperty("java.io.tmpdir"), op.getResponseId()+".xml")), output);
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(System.getProperty("java.io.tmpdir"), op.getResponseId()+".xml")), output); new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(System.getProperty("java.io.tmpdir"), op.getResponseId()+".xml")), output);
} else { } else {
@ -387,10 +391,19 @@ public class SnapShotGenerationTests {
break; break;
} }
} }
if (!ok) } catch (Exception e) {
throw e; lastOpOutcome = AssertionResponseTypes.BAD;
else }
break;
} else if (action.hasAssert()) {
SetupActionAssertComponent a = action.getAssert();
if (a.getResponse() != null) {
Assert.assertTrue(a.getLabel()+" (response): "+a.getDescription(), a.getResponse() == lastOpOutcome);
}
if (a.hasExpression()) {
boolean ok = fp.evaluateToBoolean(new StructureDefinition(), new StructureDefinition(), a.getExpression());
Assert.assertTrue(a.getLabel()+": "+a.getDescription(), ok);
}
} }
} }
} catch (Exception e) { } catch (Exception e) {

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId> <artifactId>org.hl7.fhir.core</artifactId>
<version>3.7.3-SNAPSHOT</version> <version>3.7.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId> <artifactId>org.hl7.fhir.core</artifactId>
<version>3.7.3-SNAPSHOT</version> <version>3.7.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -55,28 +55,63 @@
<build> <build>
<plugins> <plugins>
<!--
Build a fatjar containing all depenedncies
-->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-dependency-plugin</artifactId>
<version>3.2.1</version>
<executions> <executions>
<execution> <execution>
<phase>package</phase> <id>unpack-dependencies</id>
<phase>process-classes</phase>
<goals> <goals>
<goal>shade</goal> <goal>unpack-dependencies</goal>
</goals> </goals>
<configuration> <configuration>
<shadedClassifierName/> <excludes>**/module-info.class</excludes>
<transformers> <outputDirectory>${project.build.directory}/classes</outputDirectory>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <overWriteReleases>false</overWriteReleases>
<mainClass>org.hl7.fhir.r4.validation.Validator</mainClass> <overWriteSnapshots>true</overWriteSnapshots>
</transformer> <excludeScope>test</excludeScope>
</transformers> <includeScope>compile</includeScope>
<shadedArtifactAttached>true</shadedArtifactAttached>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>org.hl7.fhir.r4.validation.Validator</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<!--
These two plugins provide quality checks that don't make sense to perform
on a fatjar.
-->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.basepom.maven</groupId>
<artifactId>duplicate-finder-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>

View File

@ -1,10 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<classpath> <classpath>
<classpathentry kind="src" path="src/test/java" output="target/test-classes" including="**/*.java"/> <classpathentry including="**/*.java" kind="src" output="target/test-classes" path="src/test/java"/>
<classpathentry kind="src" path="src/test/resources" output="target/test-classes" excluding="**/*.java"/> <classpathentry excluding="**/*.java" kind="src" output="target/test-classes" path="src/test/resources"/>
<classpathentry kind="src" path="src/main/java" including="**/*.java"/> <classpathentry including="**/*.java" kind="src" path="src/main/java"/>
<classpathentry kind="src" path="src/main/resources" excluding="**/*.java"/>
<classpathentry kind="output" path="target/classes"/>
<classpathentry kind="var" path="M2_REPO/javax/xml/bind/jaxb-api/2.3.1/jaxb-api-2.3.1.jar" sourcepath="M2_REPO/javax/xml/bind/jaxb-api/2.3.1/jaxb-api-2.3.1-sources.jar"/> <classpathentry kind="var" path="M2_REPO/javax/xml/bind/jaxb-api/2.3.1/jaxb-api-2.3.1.jar" sourcepath="M2_REPO/javax/xml/bind/jaxb-api/2.3.1/jaxb-api-2.3.1-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0.jar" sourcepath="M2_REPO/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0-sources.jar"/> <classpathentry kind="var" path="M2_REPO/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0.jar" sourcepath="M2_REPO/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0-sources.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
@ -49,4 +47,5 @@
<classpathentry kind="var" path="M2_REPO/net/bytebuddy/byte-buddy/1.9.3/byte-buddy-1.9.3.jar" sourcepath="M2_REPO/net/bytebuddy/byte-buddy/1.9.3/byte-buddy-1.9.3-sources.jar"/> <classpathentry kind="var" path="M2_REPO/net/bytebuddy/byte-buddy/1.9.3/byte-buddy-1.9.3.jar" sourcepath="M2_REPO/net/bytebuddy/byte-buddy/1.9.3/byte-buddy-1.9.3-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/net/bytebuddy/byte-buddy-agent/1.9.3/byte-buddy-agent-1.9.3.jar" sourcepath="M2_REPO/net/bytebuddy/byte-buddy-agent/1.9.3/byte-buddy-agent-1.9.3-sources.jar"/> <classpathentry kind="var" path="M2_REPO/net/bytebuddy/byte-buddy-agent/1.9.3/byte-buddy-agent-1.9.3.jar" sourcepath="M2_REPO/net/bytebuddy/byte-buddy-agent/1.9.3/byte-buddy-agent-1.9.3-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/org/objenesis/objenesis/2.6/objenesis-2.6.jar" sourcepath="M2_REPO/org/objenesis/objenesis/2.6/objenesis-2.6-sources.jar"/> <classpathentry kind="var" path="M2_REPO/org/objenesis/objenesis/2.6/objenesis-2.6.jar" sourcepath="M2_REPO/org/objenesis/objenesis/2.6/objenesis-2.6-sources.jar"/>
<classpathentry kind="output" path="target/classes"/>
</classpath> </classpath>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId> <artifactId>org.hl7.fhir.core</artifactId>
<version>3.7.3-SNAPSHOT</version> <version>3.7.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -72,7 +72,7 @@ public class BaseValidator {
*/ */
protected boolean fail(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg) { protected boolean fail(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg) {
if (!thePass) { if (!thePass) {
errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.FATAL)); addValidationMessage(errors, type, line, col, path, msg, IssueSeverity.FATAL);
} }
return thePass; return thePass;
} }
@ -87,7 +87,7 @@ public class BaseValidator {
protected boolean fail(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String msg) { protected boolean fail(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String msg) {
if (!thePass) { if (!thePass) {
String path = toPath(pathParts); String path = toPath(pathParts);
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.FATAL)); addValidationMessage(errors, type, -1, -1, path, msg, IssueSeverity.FATAL);
} }
return thePass; return thePass;
} }
@ -102,7 +102,7 @@ public class BaseValidator {
protected boolean fail(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) { protected boolean fail(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass) { if (!thePass) {
String path = toPath(pathParts); String path = toPath(pathParts);
errors.add(new ValidationMessage(source, type, -1, -1, path, formatMessage(theMessage, theMessageArguments), IssueSeverity.FATAL)); addValidationMessage(errors, type, -1, -1, path, formatMessage(theMessage, theMessageArguments), IssueSeverity.FATAL);
} }
return thePass; return thePass;
} }
@ -116,7 +116,7 @@ public class BaseValidator {
*/ */
protected boolean fail(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) { protected boolean fail(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
if (!thePass) { if (!thePass) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.FATAL)); addValidationMessage(errors, type, -1, -1, path, msg, IssueSeverity.FATAL);
} }
return thePass; return thePass;
} }
@ -145,7 +145,7 @@ public class BaseValidator {
*/ */
protected boolean hint(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg) { protected boolean hint(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg) {
if (!thePass) { if (!thePass) {
errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.INFORMATION)); addValidationMessage(errors, type, line, col, path, msg, IssueSeverity.INFORMATION);
} }
return thePass; return thePass;
} }
@ -160,7 +160,7 @@ public class BaseValidator {
protected boolean hint(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) { protected boolean hint(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass) { if (!thePass) {
String message = formatMessage(theMessage, theMessageArguments); String message = formatMessage(theMessage, theMessageArguments);
errors.add(new ValidationMessage(source, type, line, col, path, message, IssueSeverity.INFORMATION)); addValidationMessage(errors, type, line, col, path, message, IssueSeverity.INFORMATION);
} }
return thePass; return thePass;
} }
@ -168,7 +168,8 @@ public class BaseValidator {
protected boolean txHint(List<ValidationMessage> errors, String txLink, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) { protected boolean txHint(List<ValidationMessage> errors, String txLink, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass) { if (!thePass) {
String message = formatMessage(theMessage, theMessageArguments); String message = formatMessage(theMessage, theMessageArguments);
errors.add(new ValidationMessage(Source.TerminologyEngine, type, line, col, path, message, IssueSeverity.INFORMATION).setTxLink(txLink)); addValidationMessage(errors, type, line, col, path, message, IssueSeverity.INFORMATION, Source.TerminologyEngine)
.setTxLink(txLink);
} }
return thePass; return thePass;
} }
@ -184,7 +185,7 @@ public class BaseValidator {
if (!thePass) { if (!thePass) {
String path = toPath(pathParts); String path = toPath(pathParts);
String message = formatMessage(theMessage, theMessageArguments); String message = formatMessage(theMessage, theMessageArguments);
errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.INFORMATION)); addValidationMessage(errors, type, -1, -1, path, message, IssueSeverity.INFORMATION);
} }
return thePass; return thePass;
} }
@ -198,7 +199,7 @@ public class BaseValidator {
*/ */
protected boolean hint(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) { protected boolean hint(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
if (!thePass) { if (!thePass) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.INFORMATION)); addValidationMessage(errors, type, -1, -1, path, msg, IssueSeverity.INFORMATION);
} }
return thePass; return thePass;
} }
@ -213,7 +214,7 @@ public class BaseValidator {
protected boolean rule(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) { protected boolean rule(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass) { if (!thePass) {
String message = formatMessage(theMessage, theMessageArguments); String message = formatMessage(theMessage, theMessageArguments);
errors.add(new ValidationMessage(source, type, line, col, path, message, IssueSeverity.ERROR)); addValidationMessage(errors, type, line, col, path, message, IssueSeverity.ERROR);
} }
return thePass; return thePass;
} }
@ -236,7 +237,7 @@ public class BaseValidator {
protected boolean rule(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String msg) { protected boolean rule(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String msg) {
if (!thePass) { if (!thePass) {
String path = toPath(pathParts); String path = toPath(pathParts);
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.ERROR)); addValidationMessage(errors, type, -1, -1, path, msg, IssueSeverity.ERROR);
} }
return thePass; return thePass;
} }
@ -252,7 +253,7 @@ public class BaseValidator {
if (!thePass) { if (!thePass) {
String path = toPath(pathParts); String path = toPath(pathParts);
String message = formatMessage(theMessage, theMessageArguments); String message = formatMessage(theMessage, theMessageArguments);
errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.ERROR)); addValidationMessage(errors, type, -1, -1, path, message, IssueSeverity.ERROR);
} }
return thePass; return thePass;
} }
@ -266,14 +267,14 @@ public class BaseValidator {
*/ */
protected boolean rule(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) { protected boolean rule(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
if (!thePass) { if (!thePass) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.ERROR)); addValidationMessage(errors, type, -1, -1, path, msg, IssueSeverity.ERROR);
} }
return thePass; return thePass;
} }
static public boolean rule(List<ValidationMessage> errors, Source source, IssueType type, String path, boolean thePass, String msg) { public boolean rule(List<ValidationMessage> errors, Source source, IssueType type, String path, boolean thePass, String msg) {
if (!thePass) { if (!thePass) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.ERROR)); addValidationMessage(errors, type, -1, -1, path, msg, IssueSeverity.ERROR, source);
} }
return thePass; return thePass;
} }
@ -287,7 +288,7 @@ public class BaseValidator {
*/ */
protected boolean rule(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) { protected boolean rule(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) {
if (!thePass) { if (!thePass) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.ERROR)); addValidationMessage(errors, type, path, msg, html, IssueSeverity.ERROR);
} }
return thePass; return thePass;
} }
@ -330,12 +331,24 @@ public class BaseValidator {
protected boolean warning(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) { protected boolean warning(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) {
if (!thePass) { if (!thePass) {
msg = formatMessage(msg, theMessageArguments); msg = formatMessage(msg, theMessageArguments);
errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.WARNING)); IssueSeverity severity = IssueSeverity.WARNING;
addValidationMessage(errors, type, line, col, path, msg, severity);
} }
return thePass; return thePass;
} }
protected void addValidationMessage(List<ValidationMessage> errors, IssueType type, int line, int col, String path, String msg, IssueSeverity theSeverity) {
Source source = this.source;
addValidationMessage(errors, type, line, col, path, msg, theSeverity, source);
}
protected ValidationMessage addValidationMessage(List<ValidationMessage> errors, IssueType type, int line, int col, String path, String msg, IssueSeverity theSeverity, Source theSource) {
ValidationMessage validationMessage = new ValidationMessage(theSource, type, line, col, path, msg, theSeverity);
errors.add(validationMessage);
return validationMessage;
}
/** /**
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
* *
@ -355,7 +368,7 @@ public class BaseValidator {
protected boolean warningOrError(boolean isError, List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) { protected boolean warningOrError(boolean isError, List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) {
if (!thePass) { if (!thePass) {
msg = formatMessage(msg, theMessageArguments); msg = formatMessage(msg, theMessageArguments);
errors.add(new ValidationMessage(source, type, line, col, path, msg, isError ? IssueSeverity.ERROR : IssueSeverity.WARNING)); addValidationMessage(errors, type, line, col, path, msg, isError ? IssueSeverity.ERROR : IssueSeverity.WARNING);
} }
return thePass; return thePass;
@ -372,7 +385,7 @@ public class BaseValidator {
if (!thePass) { if (!thePass) {
String path = toPath(pathParts); String path = toPath(pathParts);
String message = formatMessage(theMessage, theMessageArguments); String message = formatMessage(theMessage, theMessageArguments);
errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.WARNING)); addValidationMessage(errors, type, -1, -1, path, message, IssueSeverity.WARNING);
} }
return thePass; return thePass;
} }
@ -386,7 +399,7 @@ public class BaseValidator {
*/ */
protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) { protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
if (!thePass) { if (!thePass) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.WARNING)); addValidationMessage(errors, type, -1, -1, path, msg, IssueSeverity.WARNING);
} }
return thePass; return thePass;
} }
@ -400,7 +413,7 @@ public class BaseValidator {
*/ */
protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) { protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) {
if (!thePass) { if (!thePass) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.WARNING)); addValidationMessage(errors, type, path, msg, html, IssueSeverity.WARNING);
} }
return thePass; return thePass;
} }
@ -415,7 +428,7 @@ public class BaseValidator {
protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) { protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) {
if (!thePass) { if (!thePass) {
msg = formatMessage(msg, theMessageArguments); msg = formatMessage(msg, theMessageArguments);
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.WARNING)); addValidationMessage(errors, type, path, msg, html, IssueSeverity.WARNING);
} }
return thePass; return thePass;
} }
@ -431,7 +444,7 @@ public class BaseValidator {
protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) { protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) {
if (!thePass) { if (!thePass) {
msg = formatMessage(msg, theMessageArguments); msg = formatMessage(msg, theMessageArguments);
errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.INFORMATION)); addValidationMessage(errors, type, line, col, path, msg, IssueSeverity.INFORMATION);
} }
return thePass; return thePass;
@ -448,7 +461,7 @@ public class BaseValidator {
if (!thePass) { if (!thePass) {
String path = toPath(pathParts); String path = toPath(pathParts);
String message = formatMessage(theMessage, theMessageArguments); String message = formatMessage(theMessage, theMessageArguments);
errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.INFORMATION)); addValidationMessage(errors, type, -1, -1, path, message, IssueSeverity.INFORMATION);
} }
return thePass; return thePass;
} }
@ -462,7 +475,7 @@ public class BaseValidator {
*/ */
protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) { protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
if (!thePass) { if (!thePass) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.INFORMATION)); addValidationMessage(errors, type, -1, -1, path, msg, IssueSeverity.INFORMATION);
} }
return thePass; return thePass;
} }
@ -476,11 +489,16 @@ public class BaseValidator {
*/ */
protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) { protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) {
if (!thePass) { if (!thePass) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.INFORMATION)); IssueSeverity severity = IssueSeverity.INFORMATION;
addValidationMessage(errors, type, path, msg, html, severity);
} }
return thePass; return thePass;
} }
protected void addValidationMessage(List<ValidationMessage> errors, IssueType type, String path, String msg, String html, IssueSeverity theSeverity) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, theSeverity));
}
/** /**
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
* *
@ -491,7 +509,7 @@ public class BaseValidator {
protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) { protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) {
if (!thePass) { if (!thePass) {
msg = formatMessage(msg, theMessageArguments); msg = formatMessage(msg, theMessageArguments);
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.INFORMATION)); addValidationMessage(errors, type, path, msg, html, IssueSeverity.INFORMATION);
} }
return thePass; return thePass;
} }

View File

@ -0,0 +1,216 @@
package org.hl7.fhir.r4.validation;
import java.util.*;
import java.util.stream.*;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.elementmodel.Element;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Questionnaire.*;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
/**
* Evaluates Questionnaire.item.enableWhen against a QuestionnaireResponse.
* Ignores possible modifierExtensions and extensions.
*
*/
public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator {
public static final String LINKID_ELEMENT = "linkId";
public static final String ITEM_ELEMENT = "item";
public static final String ANSWER_ELEMENT = "answer";
@Override
public boolean isQuestionEnabled(QuestionnaireItemComponent questionnaireItem, Element questionnaireResponse) {
if (!questionnaireItem.hasEnableWhen()) {
return true;
}
List<EnableWhenResult> evaluationResults = questionnaireItem.getEnableWhen()
.stream()
.map(enableCondition -> evaluateCondition(enableCondition, questionnaireResponse,
questionnaireItem.getLinkId()))
.collect(Collectors.toList());
return checkConditionResults(evaluationResults, questionnaireItem);
}
public boolean checkConditionResults(List<EnableWhenResult> evaluationResults,
QuestionnaireItemComponent questionnaireItem) {
if (questionnaireItem.hasEnableBehavior() && questionnaireItem.getEnableBehavior() == EnableWhenBehavior.ANY){
return evaluationResults.stream().anyMatch(EnableWhenResult::isEnabled);
} if (questionnaireItem.hasEnableBehavior() && questionnaireItem.getEnableBehavior() == EnableWhenBehavior.ALL){
return evaluationResults.stream().allMatch(EnableWhenResult::isEnabled);
}
//TODO: Throw exception? enableBehavior is mandatory when there are multiple conditions
return true;
}
protected EnableWhenResult evaluateCondition(QuestionnaireItemEnableWhenComponent enableCondition,
Element questionnaireResponse, String linkId) {
//TODO: Fix EnableWhenResult stuff
List<Element> answerItems = findQuestionAnswers(questionnaireResponse,
enableCondition.getQuestion());
QuestionnaireItemOperator operator = enableCondition.getOperator();
if (operator == QuestionnaireItemOperator.EXISTS){
Type answer = enableCondition.getAnswer();
if (!(answer instanceof BooleanType)){
throw new UnprocessableEntityException("Exists-operator requires answerBoolean");
}
return new EnableWhenResult(((BooleanType)answer).booleanValue() != answerItems.isEmpty(),
linkId, enableCondition, questionnaireResponse);
}
boolean result = answerItems
.stream()
.anyMatch(answer -> evaluateAnswer(answer, enableCondition.getAnswer(), enableCondition.getOperator()));
return new EnableWhenResult(result, linkId, enableCondition, questionnaireResponse);
}
public Type convertToType(Element element) throws FHIRException {
Type b = new Factory().create(element.fhirType());
if (b instanceof PrimitiveType) {
((PrimitiveType<?>) b).setValueAsString(element.primitiveValue());
} else {
for (Element child : element.getChildren()) {
if (!isExtension(child)) {
b.setProperty(child.getName(), convertToType(child));
}
}
}
return b;
}
private boolean isExtension(Element element) {
return "Extension".equals(element.fhirType());
}
protected boolean evaluateAnswer(Element answer, Type expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
Type actualAnswer;
if (isExtension(answer)) {
return false;
}
try {
actualAnswer = convertToType(answer);
} catch (FHIRException e) {
throw new UnprocessableEntityException("Unexpected answer type", e);
}
if (!actualAnswer.getClass().equals(expectedAnswer.getClass())) {
throw new UnprocessableEntityException("Expected answer and actual answer have incompatible types");
}
if (expectedAnswer instanceof Coding) {
return compareCodingAnswer((Coding)expectedAnswer, (Coding)actualAnswer, questionnaireItemOperator);
} else if ((expectedAnswer instanceof PrimitiveType)) {
return comparePrimitiveAnswer((PrimitiveType<?>)actualAnswer, (PrimitiveType<?>)expectedAnswer, questionnaireItemOperator);
} else if (expectedAnswer instanceof Quantity) {
return compareQuantityAnswer((Quantity)actualAnswer, (Quantity)expectedAnswer, questionnaireItemOperator);
}
// TODO: Attachment, reference?
throw new UnprocessableEntityException("Unimplemented answer type: " + expectedAnswer.getClass());
}
private boolean compareQuantityAnswer(Quantity actualAnswer, Quantity expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
return compareComparable(actualAnswer.getValue(), expectedAnswer.getValue(), questionnaireItemOperator);
}
private boolean comparePrimitiveAnswer(PrimitiveType<?> actualAnswer, PrimitiveType<?> expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
if (actualAnswer.getValue() instanceof Comparable){
return compareComparable((Comparable)actualAnswer.getValue(), (Comparable) expectedAnswer.getValue(), questionnaireItemOperator);
} else if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){
return actualAnswer.equalsShallow(expectedAnswer);
} else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){
return !actualAnswer.equalsShallow(expectedAnswer);
}
throw new UnprocessableEntityException("Bad operator for PrimitiveType comparison");
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private boolean compareComparable(Comparable actual, Comparable expected,
QuestionnaireItemOperator questionnaireItemOperator) {
int result = actual.compareTo(expected);
if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){
return result == 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){
return result != 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.GREATER_OR_EQUAL){
return result >= 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.LESS_OR_EQUAL){
return result <= 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.LESS_THAN){
return result < 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.GREATER_THAN){
return result > 0;
}
throw new UnprocessableEntityException("Bad operator for PrimitiveType comparison");
}
private List<Element> findQuestionAnswers(Element questionnaireResponse, String question) {
List<Element> matchingItems = questionnaireResponse.getChildren(ITEM_ELEMENT)
.stream()
.flatMap(i -> findSubItems(i).stream())
.filter(i -> hasLinkId(i, question))
.collect(Collectors.toList());
return matchingItems
.stream()
.flatMap(e -> extractAnswer(e).stream())
.collect(Collectors.toList());
}
private List<Element> extractAnswer(Element item) {
return item.getChildrenByName(ANSWER_ELEMENT)
.stream()
.flatMap(c -> c.getChildren().stream())
.collect(Collectors.toList());
}
private boolean compareCodingAnswer(Coding expectedAnswer, Coding actualAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
boolean result = compareSystems(expectedAnswer, actualAnswer) && compareCodes(expectedAnswer, actualAnswer);
if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){
return result == true;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){
return result == false;
}
throw new UnprocessableEntityException("Bad operator for Coding comparison");
}
private boolean compareCodes(Coding expectedCoding, Coding value) {
if (expectedCoding.hasCode() != value.hasCode()) {
return false;
}
if (expectedCoding.hasCode()) {
return expectedCoding.getCode().equals(value.getCode());
}
return true;
}
private boolean compareSystems(Coding expectedCoding, Coding value) {
if (expectedCoding.hasSystem() && !value.hasSystem()) {
return false;
}
if (expectedCoding.hasSystem()) {
return expectedCoding.getSystem().equals(value.getSystem());
}
return true;
}
private List<Element> findSubItems(Element item) {
List<Element> results = item.getChildren(LINKID_ELEMENT)
.stream()
.flatMap(i -> findSubItems(i).stream())
.collect(Collectors.toList());
results.add(item);
return results;
}
private boolean hasLinkId(Element item, String linkId) {
Element linkIdChild = item.getNamedChild(LINKID_ELEMENT);
if (linkIdChild != null && linkIdChild.getValue().equals(linkId)){
return true;
}
return false;
}
}

View File

@ -0,0 +1,48 @@
package org.hl7.fhir.r4.validation;
import org.hl7.fhir.r4.elementmodel.Element;
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemEnableWhenComponent;
public class EnableWhenResult {
private final boolean enabled;
private final QuestionnaireItemEnableWhenComponent enableWhenCondition;
private final Element answerItem;
private final String linkId;
/**
* Evaluation result of enableWhen condition
*
* @param enabled
* Evaluation result
* @param linkId
* LinkId of the questionnaire item
* @param enableWhenCondition
* Evaluated enableWhen condition
* @param responseItem
* item in QuestionnaireResponse
*/
public EnableWhenResult(boolean enabled, String linkId, QuestionnaireItemEnableWhenComponent enableWhenCondition,
Element answerItem) {
this.enabled = enabled;
this.linkId = linkId;
this.answerItem = answerItem;
this.enableWhenCondition = enableWhenCondition;
}
public boolean isEnabled() {
return enabled;
}
public String getLinkId() {
return linkId;
}
public Element getAnswerItem() {
return answerItem;
}
public QuestionnaireItemEnableWhenComponent getEnableWhenCondition() {
return enableWhenCondition;
}
}

View File

@ -0,0 +1,10 @@
package org.hl7.fhir.r4.validation;
import org.hl7.fhir.r4.elementmodel.Element;
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent;
public interface IEnableWhenEvaluator {
public boolean isQuestionEnabled(QuestionnaireItemComponent questionnaireItem,
Element questionnaireResponse);
}

View File

@ -35,6 +35,8 @@ import java.util.UUID;
import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.exceptions.*;
import org.hl7.fhir.convertors.VersionConvertorConstants; import org.hl7.fhir.convertors.VersionConvertorConstants;
import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
@ -275,6 +277,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private boolean noExtensibleWarnings; private boolean noExtensibleWarnings;
private String serverBase; private String serverBase;
private IEnableWhenEvaluator myEnableWhenEvaluator = new DefaultEnableWhenEvaluator();
/* /*
* Keeps track of whether a particular profile has been checked or not yet * Keeps track of whether a particular profile has been checked or not yet
*/ */
@ -2499,6 +2503,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
this.allowXsiLocation = allowXsiLocation; this.allowXsiLocation = allowXsiLocation;
} }
public void setEnableWhenEvaluator(IEnableWhenEvaluator myEnableWhenEvaluator) {
this.myEnableWhenEvaluator = myEnableWhenEvaluator;
}
/** /**
* *
* @param element * @param element
@ -2776,21 +2784,21 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
sdTime = sdTime + (System.nanoTime() - t); sdTime = sdTime + (System.nanoTime() - t);
if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, "The questionnaire \""+questionnaire+"\" could not be resolved, so no validation can be performed against the base questionnaire")) { if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, "The questionnaire \""+questionnaire+"\" could not be resolved, so no validation can be performed against the base questionnaire")) {
boolean inProgress = "in-progress".equals(element.getNamedChildValue("status")); boolean inProgress = "in-progress".equals(element.getNamedChildValue("status"));
validateQuestionannaireResponseItems(qsrc, qsrc.getItem(), errors, element, stack, inProgress); validateQuestionannaireResponseItems(qsrc, qsrc.getItem(), errors, element, stack, inProgress, element);
} }
} }
} }
private void validateQuestionannaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List<ValidationMessage> errors, Element element, NodeStack stack, boolean inProgress) { private void validateQuestionannaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List<ValidationMessage> errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot) {
String text = element.getNamedChildValue("text"); String text = element.getNamedChildValue("text");
rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), Utilities.noString(text) || text.equals(qItem.getText()), "If text exists, it must match the questionnaire definition for linkId "+qItem.getLinkId()); rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), Utilities.noString(text) || text.equals(qItem.getText()), "If text exists, it must match the questionnaire definition for linkId "+qItem.getLinkId());
List<Element> answers = new ArrayList<Element>(); List<Element> answers = new ArrayList<Element>();
element.getNamedChildren("answer", answers); element.getNamedChildren("answer", answers);
if (inProgress) if (inProgress)
warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), (answers.size() > 0) || !qItem.getRequired(), "No response answer found for required item "+qItem.getLinkId()); warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers), "No response answer found for required item "+qItem.getLinkId());
else else
rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), (answers.size() > 0) || !qItem.getRequired(), "No response answer found for required item "+qItem.getLinkId()); rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers), "No response answer found for required item "+qItem.getLinkId());
if (answers.size() > 1) if (answers.size() > 1)
rule(errors, IssueType.INVALID, answers.get(1).line(), answers.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response answer item with this linkId allowed"); rule(errors, IssueType.INVALID, answers.get(1).line(), answers.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response answer item with this linkId allowed");
@ -2865,7 +2873,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// no validation // no validation
break; break;
} }
validateQuestionannaireResponseItems(qsrc, qItem.getItem(), errors, answer, stack, inProgress); validateQuestionannaireResponseItems(qsrc, qItem.getItem(), errors, answer, stack, inProgress, questionnaireResponseRoot);
} }
if (qItem.getType() == null) { if (qItem.getType() == null) {
fail(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, "Definition for item "+qItem.getLinkId() + " does not contain a type"); fail(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, "Definition for item "+qItem.getLinkId() + " does not contain a type");
@ -2874,16 +2882,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
element.getNamedChildren("item", items); element.getNamedChildren("item", items);
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), items.isEmpty(), "Items not of type DISPLAY should not have items - linkId {0}", qItem.getLinkId()); rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), items.isEmpty(), "Items not of type DISPLAY should not have items - linkId {0}", qItem.getLinkId());
} else { } else {
validateQuestionannaireResponseItems(qsrc, qItem.getItem(), errors, element, stack, inProgress); validateQuestionannaireResponseItems(qsrc, qItem.getItem(), errors, element, stack, inProgress, questionnaireResponseRoot);
} }
} }
private void validateQuestionannaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List<ValidationMessage> errors, List<Element> elements, NodeStack stack, boolean inProgress) { private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, List<Element> answers) {
return !answers.isEmpty() || !qItem.getRequired() || qItem.getType() == QuestionnaireItemType.GROUP;
}
private void validateQuestionannaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List<ValidationMessage> errors, List<Element> elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot) {
if (elements.size() > 1) if (elements.size() > 1)
rule(errors, IssueType.INVALID, elements.get(1).line(), elements.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response item with this linkId allowed - " + qItem.getLinkId()); rule(errors, IssueType.INVALID, elements.get(1).line(), elements.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response item with this linkId allowed - " + qItem.getLinkId());
for (Element element : elements) { for (Element element : elements) {
NodeStack ns = stack.push(element, -1, null, null); NodeStack ns = stack.push(element, -1, null, null);
validateQuestionannaireResponseItem(qsrc, qItem, errors, element, ns, inProgress); validateQuestionannaireResponseItem(qsrc, qItem, errors, element, ns, inProgress, questionnaireResponseRoot);
} }
} }
@ -2895,7 +2907,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return -1; return -1;
} }
private void validateQuestionannaireResponseItems(Questionnaire qsrc, List<QuestionnaireItemComponent> qItems, List<ValidationMessage> errors, Element element, NodeStack stack, boolean inProgress) { private void validateQuestionannaireResponseItems(Questionnaire qsrc, List<QuestionnaireItemComponent> qItems, List<ValidationMessage> errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot) {
List<Element> items = new ArrayList<Element>(); List<Element> items = new ArrayList<Element>();
element.getNamedChildren("item", items); element.getNamedChildren("item", items);
// now, sort into stacks // now, sort into stacks
@ -2908,9 +2920,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (index == -1) { if (index == -1) {
QuestionnaireItemComponent qItem = findQuestionnaireItem(qsrc, linkId); QuestionnaireItemComponent qItem = findQuestionnaireItem(qsrc, linkId);
if (qItem != null) { if (qItem != null) {
rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index > -1, "Structural Error: item is in the wrong place"); rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index > -1, misplacedItemError(qItem));
NodeStack ns = stack.push(item, -1, null, null); NodeStack ns = stack.push(item, -1, null, null);
validateQuestionannaireResponseItem(qsrc, qItem, errors, element, ns, inProgress); validateQuestionannaireResponseItem(qsrc, qItem, errors, item, ns, inProgress, questionnaireResponseRoot);
} }
else else
rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId \""+linkId+"\" not found in questionnaire"); rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId \""+linkId+"\" not found in questionnaire");
@ -2919,11 +2931,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
{ {
rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex, "Structural Error: items are out of order"); rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex, "Structural Error: items are out of order");
lastIndex = index; lastIndex = index;
List<Element> mapItem = map.get(linkId);
if (mapItem == null) { List<Element> mapItem = map.computeIfAbsent(linkId, key -> new ArrayList<>());
mapItem = new ArrayList<Element>();
map.put(linkId, mapItem);
}
mapItem.add(item); mapItem.add(item);
} }
} }
@ -2932,12 +2942,24 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// ok, now we have a list of known items, grouped by linkId. We"ve made an error for anything out of order // ok, now we have a list of known items, grouped by linkId. We"ve made an error for anything out of order
for (QuestionnaireItemComponent qItem : qItems) { for (QuestionnaireItemComponent qItem : qItems) {
List<Element> mapItem = map.get(qItem.getLinkId()); List<Element> mapItem = map.get(qItem.getLinkId());
if (mapItem != null) if (mapItem != null){
validateQuestionannaireResponseItem(qsrc, qItem, errors, mapItem, stack, inProgress); rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), myEnableWhenEvaluator.isQuestionEnabled(qItem, questionnaireResponseRoot), "Item has answer, even though it is not enabled "+qItem.getLinkId());
else validateQuestionannaireResponseItem(qsrc, qItem, errors, mapItem, stack, inProgress, questionnaireResponseRoot);
} else {
//item is missing, is the question enabled?
if (myEnableWhenEvaluator.isQuestionEnabled(qItem, questionnaireResponseRoot)) {
rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), !qItem.getRequired(), "No response found for required item "+qItem.getLinkId()); rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), !qItem.getRequired(), "No response found for required item "+qItem.getLinkId());
} }
} }
}
}
private String misplacedItemError(QuestionnaireItemComponent qItem) {
return qItem.hasLinkId() ?
String.format("Structural Error: item with linkid %s is in the wrong place", qItem.getLinkId())
:
"Structural Error: item is in the wrong place";
}
private void validateQuestionnaireResponseItemQuantity( List<ValidationMessage> errors, Element answer, NodeStack stack) { private void validateQuestionnaireResponseItemQuantity( List<ValidationMessage> errors, Element answer, NodeStack stack) {
@ -3009,8 +3031,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
long t = System.nanoTime(); long t = System.nanoTime();
ValidationResult res = context.validateCode(c, vs); ValidationResult res = context.validateCode(c, vs);
txTime = txTime + (System.nanoTime() - t); txTime = txTime + (System.nanoTime() - t);
if (!res.isOk()) if (!res.isOk()) {
txRule(errors, res.getTxLink(), IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false, "The value provided ("+c.getSystem()+"::"+c.getCode()+") is not in the options value set in the questionnaire"); txRule(errors, res.getTxLink(), IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false, "The value provided (" + c.getSystem() + "::" + c.getCode() + ") is not in the options value set in the questionnaire");
} else if (res.getSeverity() != null) {
super.addValidationMessage(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), res.getMessage(), res.getSeverity(), Source.TerminologyEngine);
}
} catch (Exception e) { } catch (Exception e) {
warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false, "Error " + e.getMessage() + " validating Coding against Questionnaire Options"); warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false, "Error " + e.getMessage() + " validating Coding against Questionnaire Options");
} }

View File

@ -13,7 +13,7 @@
each other. It is fine to bump the point version of this POM without affecting each other. It is fine to bump the point version of this POM without affecting
HAPI FHIR. HAPI FHIR.
--> -->
<version>3.7.3-SNAPSHOT</version> <version>3.7.4-SNAPSHOT</version>
<properties> <properties>
<hapi_fhir_version>3.7.0-SNAPSHOT</hapi_fhir_version> <hapi_fhir_version>3.7.0-SNAPSHOT</hapi_fhir_version>