Support displayLanguage through validator framework

This commit is contained in:
Grahame Grieve 2019-06-16 14:02:09 +10:00
parent 4ff9e0626a
commit 23eb7282bd
18 changed files with 278 additions and 70 deletions

View File

@ -82,6 +82,7 @@ import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.NarrativeGenerator; import org.hl7.fhir.r5.utils.NarrativeGenerator;
import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.ToolingExtensions;
@ -215,6 +216,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private ProfileKnowledgeProvider pkp; private ProfileKnowledgeProvider pkp;
private boolean igmode; private boolean igmode;
private boolean exception; private boolean exception;
private TerminologyServiceOptions terminologyServiceOptions = new TerminologyServiceOptions();
public ProfileUtilities(IWorkerContext context, List<ValidationMessage> messages, ProfileKnowledgeProvider pkp) { public ProfileUtilities(IWorkerContext context, List<ValidationMessage> messages, ProfileKnowledgeProvider pkp) {
super(); super();
@ -2787,13 +2789,13 @@ public class ProfileUtilities extends TranslatingUtilities {
private Piece describeCoded(HierarchicalTableGenerator gen, Type fixed) { private Piece describeCoded(HierarchicalTableGenerator gen, Type fixed) {
if (fixed instanceof Coding) { if (fixed instanceof Coding) {
Coding c = (Coding) fixed; Coding c = (Coding) fixed;
ValidationResult vr = context.validateCode(c.getSystem(), c.getCode(), c.getDisplay()); ValidationResult vr = context.validateCode(terminologyServiceOptions , c.getSystem(), c.getCode(), c.getDisplay());
if (vr.getDisplay() != null) if (vr.getDisplay() != null)
return gen.new Piece(null, " ("+vr.getDisplay()+")", null).addStyle("color: darkgreen"); return gen.new Piece(null, " ("+vr.getDisplay()+")", null).addStyle("color: darkgreen");
} else if (fixed instanceof CodeableConcept) { } else if (fixed instanceof CodeableConcept) {
CodeableConcept cc = (CodeableConcept) fixed; CodeableConcept cc = (CodeableConcept) fixed;
for (Coding c : cc.getCoding()) { for (Coding c : cc.getCoding()) {
ValidationResult vr = context.validateCode(c.getSystem(), c.getCode(), c.getDisplay()); ValidationResult vr = context.validateCode(terminologyServiceOptions, c.getSystem(), c.getCode(), c.getDisplay());
if (vr.getDisplay() != null) if (vr.getDisplay() != null)
return gen.new Piece(null, " ("+vr.getDisplay()+")", null).addStyle("color: darkgreen"); return gen.new Piece(null, " ("+vr.getDisplay()+")", null).addStyle("color: darkgreen");
} }
@ -4236,4 +4238,16 @@ public class ProfileUtilities extends TranslatingUtilities {
public void setThrowException(boolean exception) { public void setThrowException(boolean exception) {
this.exception = exception; this.exception = exception;
} }
public TerminologyServiceOptions getTerminologyServiceOptions() {
return terminologyServiceOptions;
}
public void setTerminologyServiceOptions(TerminologyServiceOptions terminologyServiceOptions) {
this.terminologyServiceOptions = terminologyServiceOptions;
}
} }

View File

@ -68,6 +68,7 @@ import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent;
import org.hl7.fhir.r5.terminologies.TerminologyClient; import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.r5.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r5.terminologies.ValueSetCheckerSimple; import org.hl7.fhir.r5.terminologies.ValueSetCheckerSimple;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass; import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
@ -458,39 +459,39 @@ public abstract class BaseWorkerContext implements IWorkerContext {
// --- validate code ------------------------------------------------------------------------------- // --- validate code -------------------------------------------------------------------------------
@Override @Override
public ValidationResult validateCode(String system, String code, String display) { public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display) {
Coding c = new Coding(system, code, display); Coding c = new Coding(system, code, display);
return validateCode(c, null); return validateCode(options, c, null);
} }
@Override @Override
public ValidationResult validateCode(String system, String code, String display, ValueSet vs) { public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display, ValueSet vs) {
Coding c = new Coding(system, code, display); Coding c = new Coding(system, code, display);
return validateCode(c, vs); return validateCode(options, c, vs);
} }
@Override @Override
public ValidationResult validateCode(String code, ValueSet vs) { public ValidationResult validateCode(TerminologyServiceOptions options, String code, ValueSet vs) {
Coding c = new Coding(null, code, null); Coding c = new Coding(null, code, null);
return doValidateCode(c, vs, true); return doValidateCode(options, c, vs, true);
} }
@Override @Override
public ValidationResult validateCode(String system, String code, String display, ConceptSetComponent vsi) { public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display, ConceptSetComponent vsi) {
Coding c = new Coding(system, code, display); Coding c = new Coding(system, code, display);
ValueSet vs = new ValueSet(); ValueSet vs = new ValueSet();
vs.setUrl(Utilities.makeUuidUrn()); vs.setUrl(Utilities.makeUuidUrn());
vs.getCompose().addInclude(vsi); vs.getCompose().addInclude(vsi);
return validateCode(c, vs); return validateCode(options, c, vs);
} }
@Override @Override
public ValidationResult validateCode(Coding code, ValueSet vs) { public ValidationResult validateCode(TerminologyServiceOptions options, Coding code, ValueSet vs) {
return doValidateCode(code, vs, false); return doValidateCode(options, code, vs, false);
} }
public ValidationResult doValidateCode(Coding code, ValueSet vs, boolean implySystem) { public ValidationResult doValidateCode(TerminologyServiceOptions options, Coding code, ValueSet vs, boolean implySystem) {
CacheToken cacheToken = txCache != null ? txCache.generateValidationToken(code, vs) : null; CacheToken cacheToken = txCache != null ? txCache.generateValidationToken(options, code, vs) : null;
ValidationResult res = null; ValidationResult res = null;
if (txCache != null) if (txCache != null)
res = txCache.getValidation(cacheToken); res = txCache.getValidation(cacheToken);
@ -499,7 +500,7 @@ public abstract class BaseWorkerContext implements IWorkerContext {
// ok, first we try to validate locally // ok, first we try to validate locally
try { try {
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(vs, this); ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, this);
res = vsc.validateCode(code); res = vsc.validateCode(code);
if (txCache != null) if (txCache != null)
txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT); txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
@ -520,6 +521,8 @@ public abstract class BaseWorkerContext implements IWorkerContext {
pIn.addParameter().setName("coding").setValue(code); pIn.addParameter().setName("coding").setValue(code);
if (implySystem) if (implySystem)
pIn.addParameter().setName("implySystem").setValue(new BooleanType(true)); pIn.addParameter().setName("implySystem").setValue(new BooleanType(true));
if (options != null)
options.updateParameters(pIn);
res = validateOnServer(vs, pIn); res = validateOnServer(vs, pIn);
} catch (Exception e) { } catch (Exception e) {
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog == null ? null : txLog.getLastId()); res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog == null ? null : txLog.getLastId());
@ -530,15 +533,15 @@ public abstract class BaseWorkerContext implements IWorkerContext {
} }
@Override @Override
public ValidationResult validateCode(CodeableConcept code, ValueSet vs) { public ValidationResult validateCode(TerminologyServiceOptions options, CodeableConcept code, ValueSet vs) {
CacheToken cacheToken = txCache.generateValidationToken(code, vs); CacheToken cacheToken = txCache.generateValidationToken(options, code, vs);
ValidationResult res = txCache.getValidation(cacheToken); ValidationResult res = txCache.getValidation(cacheToken);
if (res != null) if (res != null)
return res; return res;
// ok, first we try to validate locally // ok, first we try to validate locally
try { try {
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(vs, this); ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, this);
res = vsc.validateCode(code); res = vsc.validateCode(code);
txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT); txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
return res; return res;
@ -552,6 +555,8 @@ public abstract class BaseWorkerContext implements IWorkerContext {
try { try {
Parameters pIn = new Parameters(); Parameters pIn = new Parameters();
pIn.addParameter().setName("codeableConcept").setValue(code); pIn.addParameter().setName("codeableConcept").setValue(code);
if (options != null)
options.updateParameters(pIn);
res = validateOnServer(vs, pIn); res = validateOnServer(vs, pIn);
} catch (Exception e) { } catch (Exception e) {
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog.getLastId()); res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog.getLastId());

View File

@ -42,6 +42,7 @@ import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureMap; import org.hl7.fhir.r5.model.StructureMap;
import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass; import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.INarrativeGenerator; import org.hl7.fhir.r5.utils.INarrativeGenerator;
@ -371,7 +372,7 @@ public interface IWorkerContext {
* @param display * @param display
* @return * @return
*/ */
public ValidationResult validateCode(String system, String code, String display); public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display);
/** /**
* Validation of a code - consult the terminology service * Validation of a code - consult the terminology service
@ -387,10 +388,10 @@ public interface IWorkerContext {
* @param display * @param display
* @return * @return
*/ */
public ValidationResult validateCode(String system, String code, String display, ValueSet vs); public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display, ValueSet vs);
public ValidationResult validateCode(String code, ValueSet vs); public ValidationResult validateCode(TerminologyServiceOptions options, String code, ValueSet vs);
public ValidationResult validateCode(Coding code, ValueSet vs); public ValidationResult validateCode(TerminologyServiceOptions options, Coding code, ValueSet vs);
public ValidationResult validateCode(CodeableConcept code, ValueSet vs); public ValidationResult validateCode(TerminologyServiceOptions options, CodeableConcept code, ValueSet vs);
/** /**
* Validation of a code - consult the terminology service * Validation of a code - consult the terminology service
@ -406,7 +407,7 @@ public interface IWorkerContext {
* @param display * @param display
* @return * @return
*/ */
public ValidationResult validateCode(String system, String code, String display, ConceptSetComponent vsi); public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display, ConceptSetComponent vsi);
/** /**
* returns the recommended tla for the type * returns the recommended tla for the type

View File

@ -43,6 +43,7 @@ import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass; import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
@ -111,7 +112,7 @@ public class TerminologyCache {
load(); load();
} }
public CacheToken generateValidationToken(Coding code, ValueSet vs) { public CacheToken generateValidationToken(TerminologyServiceOptions options, Coding code, ValueSet vs) {
CacheToken ct = new CacheToken(); CacheToken ct = new CacheToken();
if (code.hasSystem()) if (code.hasSystem())
ct.name = getNameForSystem(code.getSystem()); ct.name = getNameForSystem(code.getSystem());
@ -121,7 +122,7 @@ public class TerminologyCache {
json.setOutputStyle(OutputStyle.PRETTY); json.setOutputStyle(OutputStyle.PRETTY);
ValueSet vsc = getVSEssense(vs); ValueSet vsc = getVSEssense(vs);
try { try {
ct.request = "{\"code\" : "+json.composeString(code, "code")+", \"valueSet\" :"+(vsc == null ? "null" : json.composeString(vsc))+"}"; ct.request = "{\"code\" : "+json.composeString(code, "code")+", \"valueSet\" :"+(vsc == null ? "null" : json.composeString(vsc))+(options == null ? "" : ", "+options.toJson())+"}";
} catch (IOException e) { } catch (IOException e) {
throw new Error(e); throw new Error(e);
} }
@ -129,7 +130,7 @@ public class TerminologyCache {
return ct; return ct;
} }
public CacheToken generateValidationToken(CodeableConcept code, ValueSet vs) { public CacheToken generateValidationToken(TerminologyServiceOptions options, CodeableConcept code, ValueSet vs) {
CacheToken ct = new CacheToken(); CacheToken ct = new CacheToken();
for (Coding c : code.getCoding()) { for (Coding c : code.getCoding()) {
if (c.hasSystem()) if (c.hasSystem())
@ -139,7 +140,7 @@ public class TerminologyCache {
json.setOutputStyle(OutputStyle.PRETTY); json.setOutputStyle(OutputStyle.PRETTY);
ValueSet vsc = getVSEssense(vs); ValueSet vsc = getVSEssense(vs);
try { try {
ct.request = "{\"code\" : "+json.composeString(code, "codeableConcept")+", \"valueSet\" :"+json.composeString(vsc)+"}"; ct.request = "{\"code\" : "+json.composeString(code, "codeableConcept")+", \"valueSet\" :"+json.composeString(vsc)+(options == null ? "" : ", "+options.toJson())+"}";
} catch (IOException e) { } catch (IOException e) {
throw new Error(e); throw new Error(e);
} }

View File

@ -0,0 +1,36 @@
package org.hl7.fhir.r5.terminologies;
import org.fhir.ucum.Utilities;
import org.hl7.fhir.r5.model.Parameters;
public class TerminologyServiceOptions {
private String language;
public TerminologyServiceOptions() {
super();
}
public TerminologyServiceOptions(String language) {
super();
this.language = language;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public String toJson() {
return "\"lang\":\""+language+"\"";
}
public void updateParameters(Parameters pIn) {
if (!Utilities.noString(language))
pIn.addParameter("displayLanguage", language);
}
}

View File

@ -51,10 +51,12 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
private ValueSet valueset; private ValueSet valueset;
private IWorkerContext context; private IWorkerContext context;
private Map<String, ValueSetCheckerSimple> inner = new HashMap<>(); private Map<String, ValueSetCheckerSimple> inner = new HashMap<>();
private TerminologyServiceOptions options;
public ValueSetCheckerSimple(ValueSet source, IWorkerContext context) { public ValueSetCheckerSimple(TerminologyServiceOptions options, ValueSet source, IWorkerContext context) {
this.valueset = source; this.valueset = source;
this.context = context; this.context = context;
this.options = options;
} }
public ValidationResult validateCode(CodeableConcept code) throws FHIRException { public ValidationResult validateCode(CodeableConcept code) throws FHIRException {
@ -425,7 +427,7 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
return inner.get(url); return inner.get(url);
} }
ValueSet vs = context.fetchResource(ValueSet.class, url); ValueSet vs = context.fetchResource(ValueSet.class, url);
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(vs, context); ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, context);
inner.put(url, vsc); inner.put(url, vsc);
return vsc; return vsc;
} }

View File

@ -47,6 +47,7 @@ import org.hl7.fhir.r5.model.TemporalPrecisionEnum;
import org.hl7.fhir.r5.model.TimeType; import org.hl7.fhir.r5.model.TimeType;
import org.hl7.fhir.r5.model.TypeDetails; import org.hl7.fhir.r5.model.TypeDetails;
import org.hl7.fhir.r5.model.TypeDetails.ProfiledType; import org.hl7.fhir.r5.model.TypeDetails.ProfiledType;
import org.hl7.fhir.r5.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException; import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails; import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails;
@ -175,6 +176,7 @@ public class FHIRPathEngine {
private Set<String> primitiveTypes = new HashSet<String>(); private Set<String> primitiveTypes = new HashSet<String>();
private Map<String, StructureDefinition> allTypes = new HashMap<String, StructureDefinition>(); private Map<String, StructureDefinition> allTypes = new HashMap<String, StructureDefinition>();
private boolean legacyMode; // some R2 and R3 constraints assume that != is valid for emptty sets, so when running for R2/R3, this is set ot true private boolean legacyMode; // some R2 and R3 constraints assume that != is valid for emptty sets, so when running for R2/R3, this is set ot true
private TerminologyServiceOptions terminologyServiceOptions = new TerminologyServiceOptions();
// if the fhir path expressions are allowed to use constants beyond those defined in the specification // if the fhir path expressions are allowed to use constants beyond those defined in the specification
// the application can implement them by providing a constant resolver // the application can implement them by providing a constant resolver
@ -1893,13 +1895,13 @@ public class FHIRPathEngine {
if (vs != null) { if (vs != null) {
for (Base l : left) { for (Base l : left) {
if (l.fhirType().equals("code")) { if (l.fhirType().equals("code")) {
if (worker.validateCode(l.castToCoding(l), vs).isOk()) if (worker.validateCode(terminologyServiceOptions , l.castToCoding(l), vs).isOk())
ans = true; ans = true;
} else if (l.fhirType().equals("Coding")) { } else if (l.fhirType().equals("Coding")) {
if (worker.validateCode(l.castToCoding(l), vs).isOk()) if (worker.validateCode(terminologyServiceOptions, l.castToCoding(l), vs).isOk())
ans = true; ans = true;
} else if (l.fhirType().equals("CodeableConcept")) { } else if (l.fhirType().equals("CodeableConcept")) {
if (worker.validateCode(l.castToCodeableConcept(l), vs).isOk()) if (worker.validateCode(terminologyServiceOptions, l.castToCodeableConcept(l), vs).isOk())
ans = true; ans = true;
} }
} }
@ -4306,5 +4308,10 @@ public class FHIRPathEngine {
private Equality boolToTriState(boolean b) { private Equality boolToTriState(boolean b) {
return b ? Equality.True : Equality.False; return b ? Equality.True : Equality.False;
} }
public TerminologyServiceOptions getTerminologyServiceOptions() {
return terminologyServiceOptions;
}
} }

View File

@ -133,6 +133,9 @@ public interface IResourceValidator {
public boolean isErrorForUnknownProfiles(); public boolean isErrorForUnknownProfiles();
public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles); public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles);
public String getValidationLanguage();
public void setValidationLanguage(String value);
/** /**
* Validate suite * Validate suite
* *

View File

@ -175,6 +175,7 @@ import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionParameterComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionParameterComponent;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext;
import org.hl7.fhir.r5.utils.LiquidEngine.LiquidDocument; import org.hl7.fhir.r5.utils.LiquidEngine.LiquidDocument;
@ -999,6 +1000,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
private List<ConceptMapRenderInstructions> renderingMaps = new ArrayList<ConceptMapRenderInstructions>(); private List<ConceptMapRenderInstructions> renderingMaps = new ArrayList<ConceptMapRenderInstructions>();
private boolean pretty; private boolean pretty;
private boolean canonicalUrlsAsLinks; private boolean canonicalUrlsAsLinks;
private TerminologyServiceOptions terminologyServiceOptions = new TerminologyServiceOptions();
public NarrativeGenerator(String prefix, String basePath, IWorkerContext context) { public NarrativeGenerator(String prefix, String basePath, IWorkerContext context) {
super(); super();
@ -1836,7 +1838,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
} }
private String lookupCode(String system, String code) { private String lookupCode(String system, String code) {
ValidationResult t = context.validateCode(system, code, null); ValidationResult t = context.validateCode(terminologyServiceOptions , system, code, null);
if (t != null && t.getDisplay() != null) if (t != null && t.getDisplay() != null)
return t.getDisplay(); return t.getDisplay();
@ -2568,7 +2570,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
private String getDisplayForConcept(String system, String value) { private String getDisplayForConcept(String system, String value) {
if (value == null || system == null) if (value == null || system == null)
return null; return null;
ValidationResult cl = context.validateCode(system, value, null); ValidationResult cl = context.validateCode(terminologyServiceOptions, system, value, null);
return cl == null ? null : cl.getDisplay(); return cl == null ? null : cl.getDisplay();
} }
@ -3765,7 +3767,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
li.ah(href).addText(f.getValue()); li.ah(href).addText(f.getValue());
} else if ("concept".equals(f.getProperty()) && inc.hasSystem()) { } else if ("concept".equals(f.getProperty()) && inc.hasSystem()) {
li.addText(f.getValue()); li.addText(f.getValue());
ValidationResult vr = context.validateCode(inc.getSystem(), f.getValue(), null); ValidationResult vr = context.validateCode(terminologyServiceOptions, inc.getSystem(), f.getValue(), null);
if (vr.isOk()) { if (vr.isOk()) {
li.tx(" ("+vr.getDisplay()+")"); li.tx(" ("+vr.getDisplay()+")");
} }
@ -3847,7 +3849,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
} }
} }
return context.validateCode(inc.getSystem(), code, null).asConceptDefinition(); return context.validateCode(terminologyServiceOptions, inc.getSystem(), code, null).asConceptDefinition();
} }
@ -4752,5 +4754,13 @@ public class NarrativeGenerator implements INarrativeGenerator {
return this; return this;
} }
public TerminologyServiceOptions getTerminologyServiceOptions() {
return terminologyServiceOptions;
}
public void setTerminologyServiceOptions(TerminologyServiceOptions terminologyServiceOptions) {
this.terminologyServiceOptions = terminologyServiceOptions;
}
} }

View File

@ -102,6 +102,7 @@ import org.hl7.fhir.r5.model.TypeDetails.ProfiledType;
import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException; import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext;
@ -221,7 +222,8 @@ public class StructureMapUtilities {
private FHIRPathEngine fpe; private FHIRPathEngine fpe;
private ITransformerServices services; private ITransformerServices services;
private ProfileKnowledgeProvider pkp; private ProfileKnowledgeProvider pkp;
private Map<String, Integer> ids = new HashMap<String, Integer>(); private Map<String, Integer> ids = new HashMap<String, Integer>();
private TerminologyServiceOptions terminologyServiceOptions = new TerminologyServiceOptions();
public StructureMapUtilities(IWorkerContext worker, ITransformerServices services, ProfileKnowledgeProvider pkp) { public StructureMapUtilities(IWorkerContext worker, ITransformerServices services, ProfileKnowledgeProvider pkp) {
super(); super();
@ -1965,7 +1967,7 @@ public class StructureMapUtilities {
throw new FHIRException("The code '"+code+"' is not in the value set '"+uri+"' (valid codes: "+b.toString()+"; also checked displays)"); throw new FHIRException("The code '"+code+"' is not in the value set '"+uri+"' (valid codes: "+b.toString()+"; also checked displays)");
} else } else
system = uri; system = uri;
ValidationResult vr = worker.validateCode(system, code, null); ValidationResult vr = worker.validateCode(terminologyServiceOptions, system, code, null);
if (vr != null && vr.getDisplay() != null) if (vr != null && vr.getDisplay() != null)
display = vr.getDisplay(); display = vr.getDisplay();
return new Coding().setSystem(system).setCode(code).setDisplay(display); return new Coding().setSystem(system).setCode(code).setDisplay(display);
@ -2958,5 +2960,13 @@ public class StructureMapUtilities {
} }
return null; return null;
} }
public TerminologyServiceOptions getTerminologyServiceOptions() {
return terminologyServiceOptions;
}
public void setTerminologyServiceOptions(TerminologyServiceOptions terminologyServiceOptions) {
this.terminologyServiceOptions = terminologyServiceOptions;
}
} }

View File

@ -33,6 +33,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.Stack;
import java.util.UUID; import java.util.UUID;
import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.NotImplementedException;
@ -121,6 +122,7 @@ import org.hl7.fhir.r5.model.TypeDetails;
import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.r5.test.utils.TestingUtilities;
import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException; import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.FHIRPathEngine;
@ -264,6 +266,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private boolean noTerminologyChecks; private boolean noTerminologyChecks;
private boolean hintAboutNonMustSupport; private boolean hintAboutNonMustSupport;
private BestPracticeWarningLevel bpWarnings; private BestPracticeWarningLevel bpWarnings;
private String validationLanguage;
private List<String> extensionDomains = new ArrayList<String>(); private List<String> extensionDomains = new ArrayList<String>();
@ -821,13 +824,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
// public API // public API
private boolean checkCode(List<ValidationMessage> errors, Element element, String path, String code, String system, String display, boolean checkDisplay) throws TerminologyServiceException { private boolean checkCode(List<ValidationMessage> errors, Element element, String path, String code, String system, String display, boolean checkDisplay, NodeStack stack) throws TerminologyServiceException {
long t = System.nanoTime(); long t = System.nanoTime();
boolean ss = context.supportsSystem(system); boolean ss = context.supportsSystem(system);
txTime = txTime + (System.nanoTime() - t); txTime = txTime + (System.nanoTime() - t);
if (ss) { if (ss) {
t = System.nanoTime(); t = System.nanoTime();
ValidationResult s = context.validateCode(system, code, checkDisplay ? display : null); ValidationResult s = context.validateCode(new TerminologyServiceOptions(stack.workingLang), system, code, checkDisplay ? display : null);
txTime = txTime + (System.nanoTime() - t); txTime = txTime + (System.nanoTime() - t);
if (s == null) if (s == null)
return true; return true;
@ -935,7 +938,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
private boolean checkCodeableConcept(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext) { private boolean checkCodeableConcept(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, NodeStack stack) {
boolean res = true; boolean res = true;
if (!noTerminologyChecks && theElementCntext != null && theElementCntext.hasBinding()) { if (!noTerminologyChecks && theElementCntext != null && theElementCntext.hasBinding()) {
ElementDefinitionBindingComponent binding = theElementCntext.getBinding(); ElementDefinitionBindingComponent binding = theElementCntext.getBinding();
@ -968,7 +971,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) { if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) {
// ignore this since we can't validate but it doesn't matter.. // ignore this since we can't validate but it doesn't matter..
} else { } else {
ValidationResult vr = context.validateCode(cc, valueset); ValidationResult vr = context.validateCode(new TerminologyServiceOptions(stack.workingLang), cc, valueset);
res = false; res = false;
if (!vr.isOk()) { if (!vr.isOk()) {
bindingsOk = false; bindingsOk = false;
@ -977,7 +980,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code from this value set is required (class = "+vr.getErrorClass().toString()+")"); txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code from this value set is required (class = "+vr.getErrorClass().toString()+")");
else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { else if (binding.getStrength() == BindingStrength.EXTENSIBLE) {
if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"))
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), cc); checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), cc, stack);
else if (!noExtensibleWarnings) else if (!noExtensibleWarnings)
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code should come from this value set unless it has no suitable code (class = "+vr.getErrorClass().toString()+")"); txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code should come from this value set unless it has no suitable code (class = "+vr.getErrorClass().toString()+")");
} else if (binding.getStrength() == BindingStrength.PREFERRED) } else if (binding.getStrength() == BindingStrength.PREFERRED)
@ -987,7 +990,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()+", and a code from this value set is required) (codes = "+ccSummary(cc)+")"); txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()+", and a code from this value set is required) (codes = "+ccSummary(cc)+")");
else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { else if (binding.getStrength() == BindingStrength.EXTENSIBLE) {
if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"))
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), cc); checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), cc, stack);
else if (!noExtensibleWarnings) else if (!noExtensibleWarnings)
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code) (codes = "+ccSummary(cc)+")"); txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code) (codes = "+ccSummary(cc)+")");
} else if (binding.getStrength() == BindingStrength.PREFERRED) } else if (binding.getStrength() == BindingStrength.PREFERRED)
@ -1003,7 +1006,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
String nextCode = nextCoding.getCode(); String nextCode = nextCoding.getCode();
String nextSystem = nextCoding.getSystem(); String nextSystem = nextCoding.getSystem();
if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) {
ValidationResult vr = context.validateCode(nextSystem, nextCode, null); ValidationResult vr = context.validateCode(new TerminologyServiceOptions(stack.workingLang), nextSystem, nextCode, null);
if (!vr.isOk()) { if (!vr.isOk()) {
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Code {0} is not a valid code in code system {1}", nextCode, nextSystem); txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Code {0} is not a valid code in code system {1}", nextCode, nextSystem);
} }
@ -1027,13 +1030,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return res; return res;
} }
private void checkMaxValueSet(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, String maxVSUrl, CodeableConcept cc) { private void checkMaxValueSet(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, String maxVSUrl, CodeableConcept cc, NodeStack stack) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl());
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found")) { if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found")) {
try { try {
long t = System.nanoTime(); long t = System.nanoTime();
ValidationResult vr = context.validateCode(cc, valueset); ValidationResult vr = context.validateCode(new TerminologyServiceOptions(stack.workingLang), cc, valueset);
txTime = txTime + (System.nanoTime() - t); txTime = txTime + (System.nanoTime() - t);
if (!vr.isOk()) { if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1047,13 +1050,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
private void checkMaxValueSet(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, String maxVSUrl, Coding c) { private void checkMaxValueSet(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, String maxVSUrl, Coding c, NodeStack stack) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl());
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found")) { if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found")) {
try { try {
long t = System.nanoTime(); long t = System.nanoTime();
ValidationResult vr = context.validateCode(c, valueset); ValidationResult vr = context.validateCode(new TerminologyServiceOptions(stack.workingLang), c, valueset);
txTime = txTime + (System.nanoTime() - t); txTime = txTime + (System.nanoTime() - t);
if (!vr.isOk()) { if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1067,13 +1070,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
private void checkMaxValueSet(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, String maxVSUrl, String value) { private void checkMaxValueSet(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, String maxVSUrl, String value, NodeStack stack) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl());
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found")) { if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found")) {
try { try {
long t = System.nanoTime(); long t = System.nanoTime();
ValidationResult vr = context.validateCode(value, valueset); ValidationResult vr = context.validateCode(new TerminologyServiceOptions(stack.workingLang), value, valueset);
txTime = txTime + (System.nanoTime() - t); txTime = txTime + (System.nanoTime() - t);
if (!vr.isOk()) { if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1101,7 +1104,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
checkFixedValue(errors, path + ".userSelected", focus.getNamedChild("userSelected"), fixed.getUserSelectedElement(), "userSelected", focus); checkFixedValue(errors, path + ".userSelected", focus.getNamedChild("userSelected"), fixed.getUserSelectedElement(), "userSelected", focus);
} }
private void checkCoding(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, boolean inCodeableConcept, boolean checkDisplay) { private void checkCoding(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, boolean inCodeableConcept, boolean checkDisplay, NodeStack stack) {
String code = element.getNamedChildValue("code"); String code = element.getNamedChildValue("code");
String system = element.getNamedChildValue("system"); String system = element.getNamedChildValue("system");
String display = element.getNamedChildValue("display"); String display = element.getNamedChildValue("display");
@ -1110,7 +1113,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (system != null && code != null && !noTerminologyChecks) { if (system != null && code != null && !noTerminologyChecks) {
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(system), "The Coding references a value set, not a code system (\""+system+"\")"); rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(system), "The Coding references a value set, not a code system (\""+system+"\")");
try { try {
if (checkCode(errors, element, path, code, system, display, checkDisplay)) if (checkCode(errors, element, path, code, system, display, checkDisplay, stack))
if (theElementCntext != null && theElementCntext.hasBinding()) { if (theElementCntext != null && theElementCntext.hasBinding()) {
ElementDefinitionBindingComponent binding = theElementCntext.getBinding(); ElementDefinitionBindingComponent binding = theElementCntext.getBinding();
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, "Binding for " + path + " missing")) { if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, "Binding for " + path + " missing")) {
@ -1122,7 +1125,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
long t = System.nanoTime(); long t = System.nanoTime();
ValidationResult vr = null; ValidationResult vr = null;
if (binding.getStrength() != BindingStrength.EXAMPLE) { if (binding.getStrength() != BindingStrength.EXAMPLE) {
vr = context.validateCode(c, valueset); vr = context.validateCode(new TerminologyServiceOptions(stack.workingLang), c, valueset);
} }
txTime = txTime + (System.nanoTime() - t); txTime = txTime + (System.nanoTime() - t);
if (vr != null && !vr.isOk()) { if (vr != null && !vr.isOk()) {
@ -1133,7 +1136,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()+", and a code from this value set is required)"); txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()+", and a code from this value set is required)");
else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { else if (binding.getStrength() == BindingStrength.EXTENSIBLE) {
if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"))
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), c); checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), c, stack);
else if (!noExtensibleWarnings) else if (!noExtensibleWarnings)
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code)"); txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code)");
} else if (binding.getStrength() == BindingStrength.PREFERRED) } else if (binding.getStrength() == BindingStrength.PREFERRED)
@ -1142,7 +1145,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code is required from this value set)"+(vr.getMessage() != null ? " (error message = "+vr.getMessage()+")" : "")); txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code is required from this value set)"+(vr.getMessage() != null ? " (error message = "+vr.getMessage()+")" : ""));
else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { else if (binding.getStrength() == BindingStrength.EXTENSIBLE) {
if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"))
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), c); checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), c, stack);
else else
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code)"+(vr.getMessage() != null ? " (error message = "+vr.getMessage()+")" : "")); txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code)"+(vr.getMessage() != null ? " (error message = "+vr.getMessage()+")" : ""));
} else if (binding.getStrength() == BindingStrength.PREFERRED) } else if (binding.getStrength() == BindingStrength.PREFERRED)
@ -1495,7 +1498,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
checkFixedValue(errors, path + ".end", focus.getNamedChild("end"), fixed.getEndElement(), "end", focus); checkFixedValue(errors, path + ".end", focus.getNamedChild("end"), fixed.getEndElement(), "end", focus);
} }
private void checkPrimitive(Object appContext, List<ValidationMessage> errors, String path, String type, ElementDefinition context, Element e, StructureDefinition profile) throws FHIRException, IOException { private void checkPrimitive(Object appContext, List<ValidationMessage> errors, String path, String type, ElementDefinition context, Element e, StructureDefinition profile, NodeStack node) throws FHIRException, IOException {
if (isBlank(e.primitiveValue())) { if (isBlank(e.primitiveValue())) {
if (e.primitiveValue() == null) if (e.primitiveValue() == null)
rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), "Primitive types must have a value or must have child extensions"); rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), "Primitive types must have a value or must have child extensions");
@ -1663,7 +1666,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
if (context.hasBinding() && e.primitiveValue() != null) { if (context.hasBinding() && e.primitiveValue() != null) {
checkPrimitiveBinding(errors, path, type, context, e, profile); checkPrimitiveBinding(errors, path, type, context, e, profile, node);
} }
if (type.equals("xhtml")) { if (type.equals("xhtml")) {
@ -1730,7 +1733,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
private void checkPrimitiveBinding(List<ValidationMessage> errors, String path, String type, ElementDefinition elementContext, Element element, StructureDefinition profile) { private void checkPrimitiveBinding(List<ValidationMessage> errors, String path, String type, ElementDefinition elementContext, Element element, StructureDefinition profile, NodeStack stack) {
// We ignore bindings that aren't on string, uri or code // We ignore bindings that aren't on string, uri or code
if (!element.hasPrimitiveValue() || !("code".equals(type) || "string".equals(type) || "uri".equals(type) || "url".equals(type) || "canonical".equals(type))) { if (!element.hasPrimitiveValue() || !("code".equals(type) || "string".equals(type) || "uri".equals(type) || "url".equals(type) || "canonical".equals(type))) {
return; return;
@ -1749,7 +1752,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
long t = System.nanoTime(); long t = System.nanoTime();
ValidationResult vr = null; ValidationResult vr = null;
if (binding.getStrength() != BindingStrength.EXAMPLE) { if (binding.getStrength() != BindingStrength.EXAMPLE) {
vr = context.validateCode(value, vs); vr = context.validateCode(new TerminologyServiceOptions(stack.workingLang), value, vs);
} }
txTime = txTime + (System.nanoTime() - t); txTime = txTime + (System.nanoTime() - t);
if (vr != null && !vr.isOk()) { if (vr != null && !vr.isOk()) {
@ -1759,7 +1762,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('"+value+"') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code is required from this value set)"+(vr.getMessage() != null ? " (error message = "+vr.getMessage()+")" : "")); txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('"+value+"') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code is required from this value set)"+(vr.getMessage() != null ? " (error message = "+vr.getMessage()+")" : ""));
else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { else if (binding.getStrength() == BindingStrength.EXTENSIBLE) {
if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"))
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), value); checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), value, stack);
else if (!noExtensibleWarnings) else if (!noExtensibleWarnings)
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('"+value+"') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code should come from this value set unless it has no suitable code)"+(vr.getMessage() != null ? " (error message = "+vr.getMessage()+")" : "")); txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('"+value+"') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code should come from this value set unless it has no suitable code)"+(vr.getMessage() != null ? " (error message = "+vr.getMessage()+")" : ""));
} else if (binding.getStrength() == BindingStrength.PREFERRED) } else if (binding.getStrength() == BindingStrength.PREFERRED)
@ -2862,6 +2865,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// the instance at root is valid against the schema and schematron // the instance at root is valid against the schema and schematron
// the instance validator had no issues against the base resource profile // the instance validator had no issues against the base resource profile
private void start(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, NodeStack stack) throws FHIRException, FHIRException, IOException { private void start(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, NodeStack stack) throws FHIRException, FHIRException, IOException {
checkLang(resource, stack);
// profile is valid, and matches the resource name // profile is valid, and matches the resource name
ResourceProfiles resourceProfiles = getResourceProfiles(element, stack); ResourceProfiles resourceProfiles = getResourceProfiles(element, stack);
if (!resourceProfiles.isProcessed()) if (!resourceProfiles.isProcessed())
@ -2896,6 +2901,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
private void checkLang(Element resource, NodeStack stack) {
String lang = resource.getNamedChildValue("language");
if (!Utilities.noString(lang))
stack.workingLang = lang;
}
private void validateResourceRules(List<ValidationMessage> errors, Element element, NodeStack stack) { private void validateResourceRules(List<ValidationMessage> errors, Element element, NodeStack stack) {
String lang = element.getNamedChildValue("language"); String lang = element.getNamedChildValue("language");
Element text = element.getNamedChild("text"); Element text = element.getNamedChild("text");
@ -3256,7 +3267,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
} }
long t = System.nanoTime(); long t = System.nanoTime();
ValidationResult res = context.validateCode(c, vs); ValidationResult res = context.validateCode(new TerminologyServiceOptions(stack.workingLang), 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");
@ -3968,7 +3979,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
ei.element.markValidation(profile, ei.definition); ei.element.markValidation(profile, ei.definition);
if (type != null) { if (type != null) {
if (isPrimitiveType(type)) { if (isPrimitiveType(type)) {
checkPrimitive(hostContext, errors, ei.path, type, ei.definition, ei.element, profile); checkPrimitive(hostContext, errors, ei.path, type, ei.definition, ei.element, profile, stack);
} else { } else {
if (ei.definition.hasFixed()) { if (ei.definition.hasFixed()) {
checkFixedValue(errors,ei.path, ei.element, ei.definition.getFixed(), ei.definition.getSliceName(), null); checkFixedValue(errors,ei.path, ei.element, ei.definition.getFixed(), ei.definition.getSliceName(), null);
@ -3980,9 +3991,9 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
if (type.equals("Identifier")) { if (type.equals("Identifier")) {
checkIdentifier(errors, ei.path, ei.element, ei.definition); checkIdentifier(errors, ei.path, ei.element, ei.definition);
} else if (type.equals("Coding")) { } else if (type.equals("Coding")) {
checkCoding(errors, ei.path, ei.element, profile, ei.definition, inCodeableConcept, checkDisplayInContext); checkCoding(errors, ei.path, ei.element, profile, ei.definition, inCodeableConcept, checkDisplayInContext, stack);
} else if (type.equals("CodeableConcept")) { } else if (type.equals("CodeableConcept")) {
checkDisplay = checkCodeableConcept(errors, ei.path, ei.element, profile, ei.definition); checkDisplay = checkCodeableConcept(errors, ei.path, ei.element, profile, ei.definition, stack);
thisIsCodeableConcept = true; thisIsCodeableConcept = true;
} else if (type.equals("Reference")) { } else if (type.equals("Reference")) {
checkReference(hostContext, errors, ei.path, ei.element, profile, ei.definition, actualType, localStack); checkReference(hostContext, errors, ei.path, ei.element, profile, ei.definition, actualType, localStack);
@ -4423,13 +4434,16 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
private List<String> logicalPaths; // dotted format, various entry points private List<String> logicalPaths; // dotted format, various entry points
private NodeStack parent; private NodeStack parent;
private ElementDefinition type; private ElementDefinition type;
private String workingLang;
public NodeStack() { public NodeStack() {
workingLang = validationLanguage;
} }
public NodeStack(Element element) { public NodeStack(Element element) {
this.element = element; this.element = element;
literalPath = element.getName(); literalPath = element.getName();
workingLang = validationLanguage;
} }
public String addToLiteralPath(String... path) { public String addToLiteralPath(String... path) {
@ -4471,6 +4485,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
private NodeStack push(Element element, int count, ElementDefinition definition, ElementDefinition type) { private NodeStack push(Element element, int count, ElementDefinition definition, ElementDefinition type) {
NodeStack res = new NodeStack(); NodeStack res = new NodeStack();
res.parent = this; res.parent = this;
res.workingLang = this.workingLang;
res.element = element; res.element = element;
res.definition = definition; res.definition = definition;
res.literalPath = getLiteralPath() + "." + element.getName(); res.literalPath = getLiteralPath() + "." + element.getName();
@ -4632,5 +4647,13 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
return externalHostServices; return externalHostServices;
} }
public String getValidationLanguage() {
return validationLanguage;
}
public void setValidationLanguage(String validationLanguage) {
this.validationLanguage = validationLanguage;
}
} }

View File

@ -244,6 +244,7 @@ public class ValidationEngine {
private boolean hintAboutNonMustSupport; private boolean hintAboutNonMustSupport;
private boolean anyExtensionsAllowed = false; private boolean anyExtensionsAllowed = false;
private String version; private String version;
private String language;
private PackageCacheManager pcm; private PackageCacheManager pcm;
private PrintWriter mapLog; private PrintWriter mapLog;
@ -323,6 +324,14 @@ public class ValidationEngine {
pcm = new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION); pcm = new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION);
} }
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
private void loadDefinitions(String src) throws Exception { private void loadDefinitions(String src) throws Exception {
Map<String, byte[]> source = loadIgSource(src); Map<String, byte[]> source = loadIgSource(src);
if (version == null) if (version == null)
@ -1062,6 +1071,7 @@ public class ValidationEngine {
validator.setHintAboutNonMustSupport(hintAboutNonMustSupport); validator.setHintAboutNonMustSupport(hintAboutNonMustSupport);
validator.setAnyExtensionsAllowed(anyExtensionsAllowed); validator.setAnyExtensionsAllowed(anyExtensionsAllowed);
validator.setNoInvariantChecks(isNoInvariantChecks()); validator.setNoInvariantChecks(isNoInvariantChecks());
validator.setValidationLanguage(language);
return validator; return validator;
} }

View File

@ -165,6 +165,10 @@ public class Validator {
System.out.println(" * JSON: json.schema"); System.out.println(" * JSON: json.schema");
System.out.println(" * RDF: SHEX"); System.out.println(" * RDF: SHEX");
System.out.println(" Default: false"); System.out.println(" Default: false");
System.out.println("-language: [lang]");
System.out.println(" The language to use when validating coding displays - same value as for xml:lang");
System.out.println(" Not used if the resource specifies language");
System.out.println(" Default: no specified language");
System.out.println("-strictExtensions: If present, treat extensions not defined within the specified FHIR version and any"); System.out.println("-strictExtensions: If present, treat extensions not defined within the specified FHIR version and any");
System.out.println(" referenced implementation guides or profiles as errors. (Default is to only raise information messages.)"); System.out.println(" referenced implementation guides or profiles as errors. (Default is to only raise information messages.)");
System.out.println("-hintAboutNonMustSupport: If present, raise hints if the instance contains data elements that are not"); System.out.println("-hintAboutNonMustSupport: If present, raise hints if the instance contains data elements that are not");
@ -269,6 +273,7 @@ public class Validator {
String sv = null; String sv = null;
String txLog = null; String txLog = null;
String mapLog = null; String mapLog = null;
String lang = null;
// load the parameters - so order doesn't matter // load the parameters - so order doesn't matter
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
@ -337,6 +342,11 @@ public class Validator {
throw new Error("Specified -log without indicating file"); throw new Error("Specified -log without indicating file");
else else
mapLog = args[++i]; mapLog = args[++i];
else if (args[i].equals("-language"))
if (i+1 == args.length)
throw new Error("Specified -language without indicating language");
else
lang = args[++i];
else if (args[i].equals("-ig")) else if (args[i].equals("-ig"))
if (i+1 == args.length) if (i+1 == args.length)
throw new Error("Specified -ig without indicating ig file"); throw new Error("Specified -ig without indicating ig file");
@ -378,6 +388,7 @@ public class Validator {
validator.setNative(doNative); validator.setNative(doNative);
validator.setHintAboutNonMustSupport(hintAboutNonMustSupport); validator.setHintAboutNonMustSupport(hintAboutNonMustSupport);
validator.setAnyExtensionsAllowed(anyExtensionsAllowed); validator.setAnyExtensionsAllowed(anyExtensionsAllowed);
validator.setLanguage(lang);
IParser x; IParser x;
if (output != null && output.endsWith(".json")) if (output != null && output.endsWith(".json"))

View File

@ -94,8 +94,8 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
this.content = content; this.content = content;
} }
private static final String DEF_TX = "http://tx.fhir.org"; // private static final String DEF_TX = "http://tx.fhir.org";
private static final String DBG_TX = "http://local.fhir.org:960"; private static final String DEF_TX = "http://local.fhir.org:960";
private static ValidationEngine ve; private static ValidationEngine ve;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@ -133,6 +133,10 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
if (content.has("allowed-extension-domains")) if (content.has("allowed-extension-domains"))
for (JsonElement a : content.getAsJsonArray("allowed-extension-domains")) for (JsonElement a : content.getAsJsonArray("allowed-extension-domains"))
val.getExtensionDomains().add(a.getAsString()); val.getExtensionDomains().add(a.getAsString());
if (content.has("language"))
val.setValidationLanguage(content.get("language").getAsString());
else
val.setValidationLanguage(null);
val.setFetcher(this); val.setFetcher(this);
if (content.has("questionnaire")) { if (content.has("questionnaire")) {
ve.getContext().cacheResource(loadResource(TestUtilities.resourceNameToFile("validation-examples", content.get("questionnaire").getAsString()), v)); ve.getContext().cacheResource(loadResource(TestUtilities.resourceNameToFile("validation-examples", content.get("questionnaire").getAsString()), v));

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<Observation xmlns="http://hl7.org/fhir">
<status value="final"/>
<code>
<coding>
<system value="http://loinc.org" />
<code value="3151-8"/>
<display value="ingeademde O2" />
</coding>
</code>
<subject>
<reference value="Patient/example" />
<display value="Example Patient" />
</subject>
<effectiveDateTime value="2016-12-19T09:00:00Z" />
<valueQuantity>
<value value="8"/>
</valueQuantity>
</Observation>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<Observation xmlns="http://hl7.org/fhir">
<status value="final"/>
<code>
<coding>
<system value="http://loinc.org" />
<code value="3151-8"/>
<display value="ingeademde O2" />
</coding>
</code>
<subject>
<reference value="Patient/example" />
<display value="Example Patient" />
</subject>
<effectiveDateTime value="2016-12-19T09:00:00Z" />
<valueQuantity>
<value value="8"/>
</valueQuantity>
</Observation>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<Observation xmlns="http://hl7.org/fhir">
<language value="nl-NL"/>
<status value="final"/>
<code>
<coding>
<system value="http://loinc.org" />
<code value="3151-8"/>
<display value="ingeademde O2" />
</coding>
</code>
<subject>
<reference value="Patient/example" />
<display value="Example Patient" />
</subject>
<effectiveDateTime value="2016-12-19T09:00:00Z" />
<valueQuantity>
<value value="8"/>
</valueQuantity>
</Observation>

View File

@ -176,7 +176,7 @@
} }
}, },
"medication-atc.json": { "medication-atc.json": {
"errorCount": 1, "errorCount": 0,
"allowed-extension-domain": "https://api-v8-r4.hspconsortium.org/DrugFormulary0/open" "allowed-extension-domain": "https://api-v8-r4.hspconsortium.org/DrugFormulary0/open"
}, },
"bp.json": { "bp.json": {
@ -768,7 +768,20 @@
}, },
"bad-bundle-reference-type.xml": { "bad-bundle-reference-type.xml": {
"errorCount": 1 "errorCount": 1
} },
"loinc-lang-nl-1.xml" : {
"errorCount": 0,
"warningCount": 1
},
"loinc-lang-nl-2.xml" : {
"language" : "nl-NL",
"errorCount": 0,
"warningCount": 0
},
"loinc-lang-nl-3.xml" : {
"errorCount": 0,
"warningCount": 0
}
} }
} }
} }