Merge pull request #1358 from hapifhir/2023-07-gg-fix-fhirpath-r3
2023 07 gg fix fhirpath r3
This commit is contained in:
commit
804575fc77
|
@ -2242,7 +2242,21 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
if (Utilities.isAbsoluteUrl(typeName)) {
|
if (Utilities.isAbsoluteUrl(typeName)) {
|
||||||
return fetchResource(StructureDefinition.class, typeName);
|
return fetchResource(StructureDefinition.class, typeName);
|
||||||
} else {
|
} else {
|
||||||
return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+typeName);
|
Set<StructureDefinition> types = new HashSet<>();
|
||||||
|
types.addAll(fetchTypeDefinitions(typeName));
|
||||||
|
types.removeIf(sd -> sd.getDerivation() == TypeDerivationRule.CONSTRAINT);
|
||||||
|
if (types.size() == 0) {
|
||||||
|
return null; // throw new FHIRException("Unresolved type "+typeName+" (0)");
|
||||||
|
} else if (types.size() == 1) {
|
||||||
|
return types.iterator().next();
|
||||||
|
} else {
|
||||||
|
types.removeIf(sd -> !sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/"));
|
||||||
|
if (types.size() != 1) {
|
||||||
|
throw new FHIRException("Ambiguous type "+typeName+" ("+types.toString()+") (contact Grahame Grieve for investigation)");
|
||||||
|
} else {
|
||||||
|
return types.iterator().next();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2250,7 +2264,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
public List<StructureDefinition> fetchTypeDefinitions(String typeName) {
|
public List<StructureDefinition> fetchTypeDefinitions(String typeName) {
|
||||||
List<StructureDefinition> res = new ArrayList<>();
|
List<StructureDefinition> res = new ArrayList<>();
|
||||||
structures.listAll(res);
|
structures.listAll(res);
|
||||||
res.removeIf(sd -> !sd.hasType() || !sd.getType().equals(typeName));
|
res.removeIf(sd -> !sd.hasType() || !(sd.getType().equals(typeName) || sd.getTypeTail().equals(typeName)));
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.hl7.fhir.r5.model.NamingSystem;
|
||||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||||
import org.hl7.fhir.utilities.OIDUtils;
|
import org.hl7.fhir.utilities.OIDUtils;
|
||||||
import org.hl7.fhir.utilities.Utilities;
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
|
import org.hl7.fhir.utilities.VersionUtilities;
|
||||||
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
||||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||||
|
@ -156,8 +157,10 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
|
||||||
public Set<String> getTypeNameSet() {
|
public Set<String> getTypeNameSet() {
|
||||||
Set<String> result = new HashSet<String>();
|
Set<String> result = new HashSet<String>();
|
||||||
for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) {
|
for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) {
|
||||||
if (sd.getKind() != StructureDefinitionKind.LOGICAL && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION)
|
if (sd.getKind() != StructureDefinitionKind.LOGICAL && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION &&
|
||||||
|
VersionUtilities.versionsCompatible(context.getVersion(), sd.getFhirVersion().toCode())) {
|
||||||
result.add(sd.getName());
|
result.add(sd.getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -249,6 +249,9 @@ public class TypeDetails {
|
||||||
return true;
|
return true;
|
||||||
if (tail != null && typesContains(sd.getUrl()+"#"+sd.getType()+tail))
|
if (tail != null && typesContains(sd.getUrl()+"#"+sd.getType()+tail))
|
||||||
return true;
|
return true;
|
||||||
|
if ("http://hl7.org/fhir/StructureDefinition/string".equals(sd.getUrl()) && typesContains(FP_String)) {
|
||||||
|
return true; // this is work around for R3
|
||||||
|
}
|
||||||
if (sd.hasBaseDefinition()) {
|
if (sd.hasBaseDefinition()) {
|
||||||
if (sd.getType().equals("uri"))
|
if (sd.getType().equals("uri"))
|
||||||
sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/string");
|
sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/string");
|
||||||
|
|
|
@ -675,7 +675,7 @@ public class FHIRPathEngine {
|
||||||
if (ed.fixedType != null) {
|
if (ed.fixedType != null) {
|
||||||
types = new TypeDetails(CollectionStatus.SINGLETON, ed.fixedType);
|
types = new TypeDetails(CollectionStatus.SINGLETON, ed.fixedType);
|
||||||
} else if (ed.getDefinition().getType().isEmpty() || isAbstractType(ed.getDefinition().getType())) {
|
} else if (ed.getDefinition().getType().isEmpty() || isAbstractType(ed.getDefinition().getType())) {
|
||||||
types = new TypeDetails(CollectionStatus.SINGLETON, ctxt+"#"+t);
|
types = new TypeDetails(CollectionStatus.SINGLETON, sd.getType()+"#"+t);
|
||||||
} else {
|
} else {
|
||||||
types = new TypeDetails(CollectionStatus.SINGLETON);
|
types = new TypeDetails(CollectionStatus.SINGLETON);
|
||||||
for (TypeRefComponent tt : ed.getDefinition().getType()) {
|
for (TypeRefComponent tt : ed.getDefinition().getType()) {
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
package org.hl7.fhir.utilities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A few places in the code, we call System.exit(int) to pass on the exit code to
|
||||||
|
* other tools (for integration in scripts)
|
||||||
|
*
|
||||||
|
* But you don't want to do that while running the failing things under JUnit
|
||||||
|
* This class does two things
|
||||||
|
*
|
||||||
|
* * Remember the exit code while shutdown / cleanup happens
|
||||||
|
* * Allow for a test case to turn exiting off altogther
|
||||||
|
*
|
||||||
|
* @author grahamegrieve
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class SystemExitManager {
|
||||||
|
|
||||||
|
private static int error;
|
||||||
|
private static boolean noExit;
|
||||||
|
|
||||||
|
public static int getError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setError(int error) {
|
||||||
|
SystemExitManager.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isNoExit() {
|
||||||
|
return noExit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setNoExit(boolean noExit) {
|
||||||
|
SystemExitManager.noExit = noExit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void finish() {
|
||||||
|
if (!noExit && error > 0) {
|
||||||
|
System.exit(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -922,6 +922,8 @@ public class I18nConstants {
|
||||||
public static final String TERMINOLOGY_TX_HINT = "TERMINOLOGY_TX_HINT";
|
public static final String TERMINOLOGY_TX_HINT = "TERMINOLOGY_TX_HINT";
|
||||||
public static final String TERMINOLOGY_TX_WARNING = "TERMINOLOGY_TX_WARNING";
|
public static final String TERMINOLOGY_TX_WARNING = "TERMINOLOGY_TX_WARNING";
|
||||||
public static final String SD_ED_TYPE_PROFILE_WRONG_TYPE = "SD_ED_TYPE_PROFILE_WRONG_TYPE";
|
public static final String SD_ED_TYPE_PROFILE_WRONG_TYPE = "SD_ED_TYPE_PROFILE_WRONG_TYPE";
|
||||||
|
public static final String VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED = "VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED";
|
||||||
|
public static final String VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED = "VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -977,3 +977,5 @@ TERMINOLOGY_TX_HINT = {1}
|
||||||
TERMINOLOGY_TX_WARNING = {1}
|
TERMINOLOGY_TX_WARNING = {1}
|
||||||
SD_ED_TYPE_WRONG_TYPE_one = The element has a type {0} which is different to the type {1} on the base profile {2}
|
SD_ED_TYPE_WRONG_TYPE_one = The element has a type {0} which is different to the type {1} on the base profile {2}
|
||||||
SD_ED_TYPE_WRONG_TYPE_other = The element has a type {0} which is not in the types {1} on the base profile {2}
|
SD_ED_TYPE_WRONG_TYPE_other = The element has a type {0} which is not in the types {1} on the base profile {2}
|
||||||
|
VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED = This include has some concepts with displays and some without - check that this is what is intended
|
||||||
|
VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED = This SNOMED-CT based include has some concepts with semantic tags (FSN terms) and some without (preferred terms) - check that this is what is intended
|
||||||
|
|
|
@ -594,7 +594,9 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
|
||||||
try {
|
try {
|
||||||
loader.load(ref.getCnt());
|
loader.load(ref.getCnt());
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
System.out.println(t.getMessage());
|
if (debug) {
|
||||||
|
System.out.println("Error during round 1 scanning: "+t.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
import org.apache.commons.text.WordUtils;
|
import org.apache.commons.text.WordUtils;
|
||||||
import org.hl7.fhir.r5.terminologies.JurisdictionUtilities;
|
import org.hl7.fhir.r5.terminologies.JurisdictionUtilities;
|
||||||
import org.hl7.fhir.utilities.FileFormat;
|
import org.hl7.fhir.utilities.FileFormat;
|
||||||
|
import org.hl7.fhir.utilities.SystemExitManager;
|
||||||
import org.hl7.fhir.utilities.TimeTracker;
|
import org.hl7.fhir.utilities.TimeTracker;
|
||||||
import org.hl7.fhir.utilities.Utilities;
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
import org.hl7.fhir.utilities.VersionUtilities;
|
import org.hl7.fhir.utilities.VersionUtilities;
|
||||||
|
@ -163,7 +164,8 @@ public class ValidatorCli {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
displayHelpForDefaultTask();
|
displayHelpForDefaultTask();
|
||||||
}return;
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
readParamsAndExecuteTask(tt, tts, cliContext, args);
|
readParamsAndExecuteTask(tt, tts, cliContext, args);
|
||||||
|
@ -310,6 +312,7 @@ public class ValidatorCli {
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("Done. " + tt.report()+". Max Memory = "+Utilities.describeSize(Runtime.getRuntime().maxMemory()));
|
System.out.println("Done. " + tt.report()+". Max Memory = "+Utilities.describeSize(Runtime.getRuntime().maxMemory()));
|
||||||
|
SystemExitManager.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
private CliTask selectCliTask(CliContext cliContext, String[] params) {
|
private CliTask selectCliTask(CliContext cliContext, String[] params) {
|
||||||
|
|
|
@ -48,6 +48,7 @@ import org.hl7.fhir.r5.renderers.spreadsheets.ValueSetSpreadsheetGenerator;
|
||||||
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
|
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
|
||||||
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
||||||
import org.hl7.fhir.utilities.FhirPublication;
|
import org.hl7.fhir.utilities.FhirPublication;
|
||||||
|
import org.hl7.fhir.utilities.SystemExitManager;
|
||||||
import org.hl7.fhir.utilities.TextFile;
|
import org.hl7.fhir.utilities.TextFile;
|
||||||
import org.hl7.fhir.utilities.TimeTracker;
|
import org.hl7.fhir.utilities.TimeTracker;
|
||||||
import org.hl7.fhir.utilities.Utilities;
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
|
@ -225,7 +226,9 @@ public class ValidationService {
|
||||||
Thread.sleep(watchScanDelay);
|
Thread.sleep(watchScanDelay);
|
||||||
}
|
}
|
||||||
} while (watch != ValidatorWatchMode.NONE);
|
} while (watch != ValidatorWatchMode.NONE);
|
||||||
System.exit(ec > 0 ? 1 : 0);
|
if (ec > 0) {
|
||||||
|
SystemExitManager.setError(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int countErrors(OperationOutcome oo) {
|
private int countErrors(OperationOutcome oo) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.hl7.fhir.validation.cli.tasks;
|
package org.hl7.fhir.validation.cli.tasks;
|
||||||
|
|
||||||
|
import org.hl7.fhir.utilities.SystemExitManager;
|
||||||
import org.hl7.fhir.utilities.TimeTracker;
|
import org.hl7.fhir.utilities.TimeTracker;
|
||||||
import org.hl7.fhir.validation.cli.model.CliContext;
|
import org.hl7.fhir.validation.cli.model.CliContext;
|
||||||
import org.hl7.fhir.validation.cli.utils.Params;
|
import org.hl7.fhir.validation.cli.utils.Params;
|
||||||
|
@ -43,6 +44,7 @@ public class TxTestsTask extends StandaloneTask{
|
||||||
final String tx = Params.getParam(args, Params.TERMINOLOGY);
|
final String tx = Params.getParam(args, Params.TERMINOLOGY);
|
||||||
final String filter = Params.getParam(args, Params.FILTER);
|
final String filter = Params.getParam(args, Params.FILTER);
|
||||||
boolean ok = new TxTester(new TxTester.InternalTxLoader(source, output), tx, false).setOutput(output).execute(version, filter);
|
boolean ok = new TxTester(new TxTester.InternalTxLoader(source, output), tx, false).setOutput(output).execute(version, filter);
|
||||||
System.exit(ok ? 1 : 0);
|
SystemExitManager.setError(ok ? 1 : 0);
|
||||||
|
SystemExitManager.finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -471,11 +471,13 @@ public class StructureDefinitionValidator extends BaseValidator {
|
||||||
}
|
}
|
||||||
// if we see fixed[x] or pattern[x] applied to a repeating element, we'll give the user a hint
|
// if we see fixed[x] or pattern[x] applied to a repeating element, we'll give the user a hint
|
||||||
}
|
}
|
||||||
List<Element> constraints = element.getChildrenByName("constraint");
|
if (snapshot) { // we just don't have enough information to figure out the context in a differential
|
||||||
int cc = 0;
|
List<Element> constraints = element.getChildrenByName("constraint");
|
||||||
for (Element invariant : constraints) {
|
int cc = 0;
|
||||||
ok = validateElementDefinitionInvariant(errors, invariant, stack.push(invariant, cc, null, null), invariantMap, elements, element, element.getNamedChildValue("path"), rootPath, profileUrl) && ok;
|
for (Element invariant : constraints) {
|
||||||
cc++;
|
ok = validateElementDefinitionInvariant(errors, invariant, stack.push(invariant, cc, null, null), invariantMap, elements, element, element.getNamedChildValue("path"), rootPath, profileUrl) && ok;
|
||||||
|
cc++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
@ -504,7 +506,7 @@ public class StructureDefinitionValidator extends BaseValidator {
|
||||||
Element oldte = te;
|
Element oldte = te;
|
||||||
te = getParent(elements, te);
|
te = getParent(elements, te);
|
||||||
if (te != null) {
|
if (te != null) {
|
||||||
exp = tail(oldte, te)+"."+exp;
|
exp = tail(oldte, te)+".all("+exp+")";
|
||||||
types = getTypesForElement(elements, te);
|
types = getTypesForElement(elements, te);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -547,13 +549,17 @@ public class StructureDefinitionValidator extends BaseValidator {
|
||||||
|
|
||||||
private List<String> getTypesForElement(List<Element> elements, Element element) {
|
private List<String> getTypesForElement(List<Element> elements, Element element) {
|
||||||
List<String> types = new ArrayList<>();
|
List<String> types = new ArrayList<>();
|
||||||
for (Element tr : element.getChildrenByName("type")) {
|
if (element.hasChild("path") && !element.getNamedChildValue("path").contains(".")) {
|
||||||
String t = tr.getNamedChildValue("code");
|
types.add(element.getNamedChildValue("path"));
|
||||||
if (t != null) {
|
} else {
|
||||||
if (isAbstractType(t) && hasChildren(element, elements) ) {
|
for (Element tr : element.getChildrenByName("type")) {
|
||||||
types.add(element.getNamedChildValue("path"));
|
String t = tr.getNamedChildValue("code");
|
||||||
} else {
|
if (t != null) {
|
||||||
types.add(t);
|
if (isAbstractType(t) && hasChildren(element, elements) ) {
|
||||||
|
types.add(element.getNamedChildValue("path"));
|
||||||
|
} else {
|
||||||
|
types.add(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,79 @@ import org.hl7.fhir.utilities.validation.ValidationOptions;
|
||||||
import org.hl7.fhir.validation.BaseValidator;
|
import org.hl7.fhir.validation.BaseValidator;
|
||||||
import org.hl7.fhir.validation.TimeTracker;
|
import org.hl7.fhir.validation.TimeTracker;
|
||||||
import org.hl7.fhir.validation.instance.InstanceValidator;
|
import org.hl7.fhir.validation.instance.InstanceValidator;
|
||||||
|
import org.hl7.fhir.validation.instance.type.ValueSetValidator.SystemLevelValidator;
|
||||||
import org.hl7.fhir.validation.instance.utils.NodeStack;
|
import org.hl7.fhir.validation.instance.utils.NodeStack;
|
||||||
|
|
||||||
public class ValueSetValidator extends BaseValidator {
|
public class ValueSetValidator extends BaseValidator {
|
||||||
|
|
||||||
|
public class SystemLevelValidator {
|
||||||
|
protected List<ValidationMessage> errors;
|
||||||
|
protected Element inc;
|
||||||
|
protected NodeStack stack;
|
||||||
|
|
||||||
|
private boolean noDisplay = false;
|
||||||
|
private boolean hasDisplay = false;
|
||||||
|
protected SystemLevelValidator(List<ValidationMessage> errors, Element inc, NodeStack stack) {
|
||||||
|
super();
|
||||||
|
this.errors = errors;
|
||||||
|
this.inc = inc;
|
||||||
|
this.stack = stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkConcept(String code, String display) {
|
||||||
|
if (Utilities.noString(display)) {
|
||||||
|
noDisplay = true;
|
||||||
|
} else {
|
||||||
|
hasDisplay = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void finish() {
|
||||||
|
hint(errors, "2023-07-21", IssueType.BUSINESSRULE, inc.line(), inc.col(), stack.getLiteralPath(), !(noDisplay && hasDisplay), I18nConstants.VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SnomedCTValidator extends SystemLevelValidator {
|
||||||
|
private boolean noTag = false;
|
||||||
|
private boolean hasTag = false;
|
||||||
|
|
||||||
|
protected SnomedCTValidator(List<ValidationMessage> errors, Element inc, NodeStack stack) {
|
||||||
|
super(errors, inc, stack);
|
||||||
|
}
|
||||||
|
public void checkConcept(String code, String display) {
|
||||||
|
super.checkConcept(code, display);
|
||||||
|
if (!Utilities.noString(display)) {
|
||||||
|
boolean tagged = display.endsWith(")") && display.indexOf("(") > display.length() - 20;
|
||||||
|
if (tagged) {
|
||||||
|
hasTag = true;
|
||||||
|
} else {
|
||||||
|
noTag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void finish() {
|
||||||
|
hint(errors, "2023-07-21", IssueType.BUSINESSRULE, inc.line(), inc.col(), stack.getLiteralPath(), !(noTag && hasTag), I18nConstants.VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GeneralValidator extends SystemLevelValidator {
|
||||||
|
|
||||||
|
protected GeneralValidator(List<ValidationMessage> errors, Element inc, NodeStack stack) {
|
||||||
|
super(errors, inc, stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private SystemLevelValidator getSystemValidator(String system, List<ValidationMessage> errors, Element inc, NodeStack stack) {
|
||||||
|
if (system == null) {
|
||||||
|
return new GeneralValidator(errors, inc, stack);
|
||||||
|
}
|
||||||
|
switch (system) {
|
||||||
|
case "http://snomed.info/sct" :return new SnomedCTValidator(errors, inc, stack);
|
||||||
|
default: return new GeneralValidator(errors, inc, stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class VSCodingValidationRequest extends CodingValidationRequest {
|
public class VSCodingValidationRequest extends CodingValidationRequest {
|
||||||
|
|
||||||
private NodeStack stack;
|
private NodeStack stack;
|
||||||
|
@ -139,6 +208,8 @@ public class ValueSetValidator extends BaseValidator {
|
||||||
}
|
}
|
||||||
List<Element> concepts = include.getChildrenByName("concept");
|
List<Element> concepts = include.getChildrenByName("concept");
|
||||||
List<Element> filters = include.getChildrenByName("filter");
|
List<Element> filters = include.getChildrenByName("filter");
|
||||||
|
|
||||||
|
SystemLevelValidator slv = getSystemValidator(system, errors, include, stack);
|
||||||
if (!Utilities.noString(system)) {
|
if (!Utilities.noString(system)) {
|
||||||
boolean systemOk = true;
|
boolean systemOk = true;
|
||||||
int cc = 0;
|
int cc = 0;
|
||||||
|
@ -147,10 +218,10 @@ public class ValueSetValidator extends BaseValidator {
|
||||||
for (Element concept : concepts) {
|
for (Element concept : concepts) {
|
||||||
// we treat the first differently because we want to know if tbe system is worth validating. if it is, then we batch the rest
|
// we treat the first differently because we want to know if tbe system is worth validating. if it is, then we batch the rest
|
||||||
if (first) {
|
if (first) {
|
||||||
systemOk = validateValueSetIncludeConcept(errors, concept, stack, stack.push(concept, cc, null, null), system, version);
|
systemOk = validateValueSetIncludeConcept(errors, concept, stack, stack.push(concept, cc, null, null), system, version, slv);
|
||||||
first = false;
|
first = false;
|
||||||
} else if (systemOk) {
|
} else if (systemOk) {
|
||||||
batch.add(prepareValidateValueSetIncludeConcept(errors, concept, stack.push(concept, cc, null, null), system, version));
|
batch.add(prepareValidateValueSetIncludeConcept(errors, concept, stack.push(concept, cc, null, null), system, version, slv));
|
||||||
}
|
}
|
||||||
cc++;
|
cc++;
|
||||||
}
|
}
|
||||||
|
@ -180,19 +251,24 @@ public class ValueSetValidator extends BaseValidator {
|
||||||
|
|
||||||
int cf = 0;
|
int cf = 0;
|
||||||
for (Element filter : filters) {
|
for (Element filter : filters) {
|
||||||
if (systemOk && !validateValueSetIncludeFilter(errors, include, stack.push(filter, cf, null, null), system, version)) {
|
if (systemOk && !validateValueSetIncludeFilter(errors, include, stack.push(filter, cf, null, null), system, version, slv)) {
|
||||||
systemOk = false;
|
systemOk = false;
|
||||||
}
|
}
|
||||||
cf++;
|
cf++;
|
||||||
}
|
}
|
||||||
|
slv.finish();
|
||||||
} else {
|
} else {
|
||||||
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), filters.size() == 0 && concepts.size() == 0, I18nConstants.VALUESET_NO_SYSTEM_WARNING);
|
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), filters.size() == 0 && concepts.size() == 0, I18nConstants.VALUESET_NO_SYSTEM_WARNING);
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validateValueSetIncludeConcept(List<ValidationMessage> errors, Element concept, NodeStack stackInc, NodeStack stack, String system, String version) {
|
|
||||||
|
private boolean validateValueSetIncludeConcept(List<ValidationMessage> errors, Element concept, NodeStack stackInc, NodeStack stack, String system, String version, SystemLevelValidator slv) {
|
||||||
String code = concept.getChildValue("code");
|
String code = concept.getChildValue("code");
|
||||||
|
String display = concept.getChildValue("display");
|
||||||
|
slv.checkConcept(code, display);
|
||||||
|
|
||||||
if (version == null) {
|
if (version == null) {
|
||||||
ValidationResult vv = context.validateCode(ValidationOptions.defaults(), new Coding(system, code, null), null);
|
ValidationResult vv = context.validateCode(ValidationOptions.defaults(), new Coding(system, code, null), null);
|
||||||
if (vv.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) {
|
if (vv.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) {
|
||||||
|
@ -229,8 +305,11 @@ public class ValueSetValidator extends BaseValidator {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private VSCodingValidationRequest prepareValidateValueSetIncludeConcept(List<ValidationMessage> errors, Element concept, NodeStack stack, String system, String version) {
|
private VSCodingValidationRequest prepareValidateValueSetIncludeConcept(List<ValidationMessage> errors, Element concept, NodeStack stack, String system, String version, SystemLevelValidator slv) {
|
||||||
String code = concept.getChildValue("code");
|
String code = concept.getChildValue("code");
|
||||||
|
String display = concept.getChildValue("display");
|
||||||
|
slv.checkConcept(code, display);
|
||||||
|
|
||||||
Coding c = new Coding(system, code, null);
|
Coding c = new Coding(system, code, null);
|
||||||
if (version != null) {
|
if (version != null) {
|
||||||
c.setVersion(version);
|
c.setVersion(version);
|
||||||
|
@ -238,7 +317,11 @@ public class ValueSetValidator extends BaseValidator {
|
||||||
return new VSCodingValidationRequest(stack, c);
|
return new VSCodingValidationRequest(stack, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validateValueSetIncludeFilter(List<ValidationMessage> errors, Element include, NodeStack push, String system, String version) {
|
private boolean validateValueSetIncludeFilter(List<ValidationMessage> errors, Element filter, NodeStack push, String system, String version, SystemLevelValidator slv) {
|
||||||
|
//
|
||||||
|
// String display = concept.getChildValue("display");
|
||||||
|
// slv.checkConcept(code, display);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
|
||||||
import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
|
import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
|
||||||
import org.hl7.fhir.r5.model.Resource;
|
import org.hl7.fhir.r5.model.Resource;
|
||||||
import org.hl7.fhir.r5.model.UriType;
|
import org.hl7.fhir.r5.model.UriType;
|
||||||
|
import org.hl7.fhir.r5.model.CanonicalType;
|
||||||
import org.hl7.fhir.r5.model.ValueSet;
|
import org.hl7.fhir.r5.model.ValueSet;
|
||||||
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
|
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
|
||||||
import org.hl7.fhir.r5.model.CodeableConcept;
|
import org.hl7.fhir.r5.model.CodeableConcept;
|
||||||
|
@ -290,7 +291,7 @@ public class TerminologyServiceTests {
|
||||||
}
|
}
|
||||||
if (vm.getUnknownSystems() != null) {
|
if (vm.getUnknownSystems() != null) {
|
||||||
for (String s : vm.getUnknownSystems()) {
|
for (String s : vm.getUnknownSystems()) {
|
||||||
res.addParameter("x-caused-by-unknown-system", new UriType(s));
|
res.addParameter("x-caused-by-unknown-system", new CanonicalType(s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (vm.getIssues().size() > 0) {
|
if (vm.getIssues().size() > 0) {
|
||||||
|
|
|
@ -2,18 +2,22 @@ package org.hl7.fhir.validation.tests;
|
||||||
|
|
||||||
import org.hl7.fhir.r4.context.SimpleWorkerContext;
|
import org.hl7.fhir.r4.context.SimpleWorkerContext;
|
||||||
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
||||||
|
import org.hl7.fhir.utilities.SystemExitManager;
|
||||||
|
import org.hl7.fhir.utilities.TextFile;
|
||||||
import org.hl7.fhir.validation.ValidatorCli;
|
import org.hl7.fhir.validation.ValidatorCli;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@Disabled
|
|
||||||
public class CDAValidationTest {
|
public class CDAValidationTest {
|
||||||
|
|
||||||
private SimpleWorkerContext context;
|
private SimpleWorkerContext context;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test() throws Exception {
|
public void test() throws Exception {
|
||||||
ValidatorCli.main(new String[] {TestingUtilities.loadTestResource("ccda.xml"), "-ig", "hl7.fhir.cda"});
|
String fn = TestingUtilities.tempFile("cda", "cda.xml");
|
||||||
|
TextFile.stringToFile(TestingUtilities.loadTestResource("cda/cda-original.xml"), fn);
|
||||||
|
SystemExitManager.setNoExit(true);
|
||||||
|
ValidatorCli.main(new String[] {fn, "-ig", "hl7.cda.uv.core#current"});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -126,3 +126,20 @@ v: {
|
||||||
"system" : "urn:ietf:bcp:47"
|
"system" : "urn:ietf:bcp:47"
|
||||||
}
|
}
|
||||||
-------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------
|
||||||
|
{"code" : {
|
||||||
|
"system" : "http://unstats.un.org/unsd/methods/m49/m49.htm",
|
||||||
|
"code" : "001",
|
||||||
|
"display" : "World"
|
||||||
|
}, "url": "http://hl7.org/fhir/ValueSet/jurisdiction", "version": "5.0.0", "langs":"[en]", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"NO_MEMBERSHIP_CHECK", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||||
|
"resourceType" : "Parameters",
|
||||||
|
"parameter" : [{
|
||||||
|
"name" : "profile-url",
|
||||||
|
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||||
|
}]
|
||||||
|
}}####
|
||||||
|
v: {
|
||||||
|
"display" : "World",
|
||||||
|
"code" : "001",
|
||||||
|
"system" : "http://unstats.un.org/unsd/methods/m49/m49.htm"
|
||||||
|
}
|
||||||
|
-------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
-------------------------------------------------------------------------------------
|
||||||
|
{"code" : {
|
||||||
|
"system" : "http://unstats.un.org/unsd/methods/m49/m49.htm",
|
||||||
|
"code" : "001"
|
||||||
|
}, "url": "http://hl7.org/fhir/ValueSet/jurisdiction--2", "version": "5.0.0", "langs":"[en]", "useServer":"true", "useClient":"false", "guessSystem":"false", "valueSetMode":"CHECK_MEMERSHIP_ONLY", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||||
|
"resourceType" : "Parameters",
|
||||||
|
"parameter" : [{
|
||||||
|
"name" : "profile-url",
|
||||||
|
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||||
|
}]
|
||||||
|
}}####
|
||||||
|
v: {
|
||||||
|
"display" : "World",
|
||||||
|
"code" : "001",
|
||||||
|
"system" : "http://unstats.un.org/unsd/methods/m49/m49.htm"
|
||||||
|
}
|
||||||
|
-------------------------------------------------------------------------------------
|
||||||
|
{"code" : {
|
||||||
|
"system" : "http://unstats.un.org/unsd/methods/m49/m49.htm",
|
||||||
|
"code" : "001"
|
||||||
|
}, "valueSet" :null, "langs":"[en]", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||||
|
"resourceType" : "Parameters",
|
||||||
|
"parameter" : [{
|
||||||
|
"name" : "profile-url",
|
||||||
|
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||||
|
}]
|
||||||
|
}}####
|
||||||
|
v: {
|
||||||
|
"display" : "World",
|
||||||
|
"code" : "001",
|
||||||
|
"system" : "http://unstats.un.org/unsd/methods/m49/m49.htm"
|
||||||
|
}
|
||||||
|
-------------------------------------------------------------------------------------
|
|
@ -822,3 +822,54 @@ v: {
|
||||||
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
|
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
|
||||||
}
|
}
|
||||||
-------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------
|
||||||
|
{"code" : {
|
||||||
|
"system" : "http://snomed.info/sct",
|
||||||
|
"code" : "230993007"
|
||||||
|
}, "valueSet" :null, "langs":"[en-US, en]", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
|
||||||
|
"resourceType" : "Parameters",
|
||||||
|
"parameter" : [{
|
||||||
|
"name" : "profile-url",
|
||||||
|
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||||
|
}]
|
||||||
|
}}####
|
||||||
|
v: {
|
||||||
|
"display" : "Worsening",
|
||||||
|
"code" : "230993007",
|
||||||
|
"system" : "http://snomed.info/sct",
|
||||||
|
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
|
||||||
|
}
|
||||||
|
-------------------------------------------------------------------------------------
|
||||||
|
{"code" : {
|
||||||
|
"system" : "http://snomed.info/sct",
|
||||||
|
"code" : "385633008"
|
||||||
|
}, "valueSet" :null, "langs":"[en-US, en]", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
|
||||||
|
"resourceType" : "Parameters",
|
||||||
|
"parameter" : [{
|
||||||
|
"name" : "profile-url",
|
||||||
|
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||||
|
}]
|
||||||
|
}}####
|
||||||
|
v: {
|
||||||
|
"display" : "Improving (qualifier value)",
|
||||||
|
"code" : "385633008",
|
||||||
|
"system" : "http://snomed.info/sct",
|
||||||
|
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
|
||||||
|
}
|
||||||
|
-------------------------------------------------------------------------------------
|
||||||
|
{"code" : {
|
||||||
|
"system" : "http://snomed.info/sct",
|
||||||
|
"code" : "260388006"
|
||||||
|
}, "valueSet" :null, "langs":"[en-US, en]", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
|
||||||
|
"resourceType" : "Parameters",
|
||||||
|
"parameter" : [{
|
||||||
|
"name" : "profile-url",
|
||||||
|
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||||
|
}]
|
||||||
|
}}####
|
||||||
|
v: {
|
||||||
|
"display" : "No status change",
|
||||||
|
"code" : "260388006",
|
||||||
|
"system" : "http://snomed.info/sct",
|
||||||
|
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
|
||||||
|
}
|
||||||
|
-------------------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in New Issue