diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java index b6e343760..eb879efea 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java @@ -22,11 +22,14 @@ package org.hl7.fhir.r5.terminologies; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; +import org.hl7.fhir.r5.model.CanonicalType; import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.CodeSystem.CodeSystemContentMode; import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent; @@ -36,7 +39,9 @@ import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent; +import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceDesignationComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; +import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; @@ -45,6 +50,7 @@ public class ValueSetCheckerSimple implements ValueSetChecker { private ValueSet valueset; private IWorkerContext context; + private Map inner = new HashMap<>(); public ValueSetCheckerSimple(ValueSet source, IWorkerContext context) { this.valueset = source; @@ -159,7 +165,47 @@ public class ValueSetCheckerSimple implements ValueSetChecker { if (code.getDisplay().equalsIgnoreCase(ds.getValue())) return new ValidationResult(cc); } - return new ValidationResult(IssueSeverity.WARNING, "Display Name for "+code.getSystem()+"#"+code.getCode()+" must be one of '"+b.toString()+"'", cc); + // also check to see if the value set has another display + ConceptReferenceComponent vs = findValueSetRef(code.getSystem(), code.getCode()); + if (vs != null && vs.hasDisplay()) { + b.append(vs.getDisplay()); + if (code.getDisplay().equalsIgnoreCase(vs.getDisplay())) + return new ValidationResult(cc); + for (ConceptReferenceDesignationComponent ds : vs.getDesignation()) { + b.append(ds.getValue()); + if (code.getDisplay().equalsIgnoreCase(ds.getValue())) + return new ValidationResult(cc); + } + } + return new ValidationResult(IssueSeverity.WARNING, "Display Name for "+code.getSystem()+"#"+code.getCode()+" should be one of '"+b.toString()+"'", cc); + } + + private ConceptReferenceComponent findValueSetRef(String system, String code) { + if (valueset == null) + return null; + // if it has an expansion + for (ValueSetExpansionContainsComponent exp : valueset.getExpansion().getContains()) { + if (system.equals(exp.getSystem()) && code.equals(exp.getCode())) { + ConceptReferenceComponent cc = new ConceptReferenceComponent(); + cc.setDisplay(exp.getDisplay()); + cc.setDesignation(cc.getDesignation()); + return cc; + } + } + for (ConceptSetComponent inc : valueset.getCompose().getInclude()) { + if (system.equals(inc.getSystem())) { + for (ConceptReferenceComponent cc : inc.getConcept()) { + if (cc.getCode().equals(code)) + return cc; + } + } + for (CanonicalType url : inc.getValueSet()) { + ConceptReferenceComponent cc = getVs(url.asStringValue()).findValueSetRef(system, code); + if (cc != null) + return cc; + } + } + return null; } private String gen(Coding code) { @@ -295,15 +341,61 @@ public class ValueSetCheckerSimple implements ValueSetChecker { if (!system.equals(vsi.getSystem())) return false; - if (vsi.hasFilter()) - throw new FHIRException("Filters - not done yet"); - + if (vsi.hasFilter()) { + boolean ok = true; + for (ConceptSetFilterComponent f : vsi.getFilter()) + if (!codeInFilter(system, f, code)) { + ok = false; + break; + } + if (ok) + return true; + } + CodeSystem def = context.fetchCodeSystem(system); if (def.getContent() != CodeSystemContentMode.COMPLETE) throw new FHIRException("Unable to resolve system "+vsi.getSystem()+" - system is not complete"); List list = def.getConcept(); - return validateCodeInConceptList(code, def, list); + boolean ok = validateCodeInConceptList(code, def, list); + if (ok && vsi.hasConcept()) { + for (ConceptReferenceComponent cc : vsi.getConcept()) + if (cc.getCode().equals(code)) + return true; + return false; + } else + return ok; + } + + private boolean codeInFilter(String system, ConceptSetFilterComponent f, String code) throws FHIRException { + CodeSystem cs = context.fetchCodeSystem(system); + if (cs == null) + throw new FHIRException("Unable to evaluate filters on unknown code system '"+system+"'"); + if ("concept".equals(f.getProperty())) + return codeInConceptFilter(cs, f, code); + else { + System.out.println("todo: handle filters with property = "+f.getProperty()); + throw new FHIRException("Unable to handle system "+cs.getUrl()+" filter with property = "+f.getProperty()); + } + } + + private boolean codeInConceptFilter(CodeSystem cs, ConceptSetFilterComponent f, String code) throws FHIRException { + switch (f.getOp()) { + case ISA: return codeInConceptIsAFilter(cs, f, code); + default: + System.out.println("todo: handle concept filters with op = "+f.getOp()); + throw new FHIRException("Unable to handle system "+cs.getUrl()+" concept filter with op = "+f.getOp()); + } + } + + private boolean codeInConceptIsAFilter(CodeSystem cs, ConceptSetFilterComponent f, String code) { + if (code.equals(f.getProperty())) + return true; + ConceptDefinitionComponent cc = findCodeInConcept(cs.getConcept(), f.getValue()); + if (cc == null) + return false; + cc = findCodeInConcept(cc.getConcept(), code); + return cc != null; } public boolean validateCodeInConceptList(String code, CodeSystem def, List list) { @@ -324,11 +416,19 @@ public class ValueSetCheckerSimple implements ValueSetChecker { } return false; } + + private ValueSetCheckerSimple getVs(String url) { + if (inner.containsKey(url)) { + return inner.get(url); + } + ValueSet vs = context.fetchResource(ValueSet.class, url); + ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(vs, context); + inner.put(url, vsc); + return vsc; + } private boolean inImport(String uri, String system, String code) throws FHIRException { - ValueSet vs = context.fetchResource(ValueSet.class, uri); - ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(vs, context); - return vsc.codeInValueSet(system, code); + return getVs(uri).codeInValueSet(system, code); } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java index aae2f7efc..5e4fa4456 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java @@ -4130,20 +4130,24 @@ public class FHIRPathEngine { element = slice; } - List childDefinitions; - childDefinitions = ProfileUtilities.getChildMap(sd, element); - // if that's empty, get the children of the type - if (childDefinitions.isEmpty()) { + if (expr.getName().equals("$this")) { + focus = element; + } else { + List childDefinitions; + childDefinitions = ProfileUtilities.getChildMap(sd, element); + // if that's empty, get the children of the type + if (childDefinitions.isEmpty()) { - sd = fetchStructureByType(element); - if (sd == null) - throw new DefinitionException("Problem with use of resolve() - profile '"+element.getType().get(0).getProfile()+"' on "+element.getId()+" could not be resolved"); - childDefinitions = ProfileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep()); - } - for (ElementDefinition t : childDefinitions) { - if (tailMatches(t, expr.getName())) { - focus = t; - break; + sd = fetchStructureByType(element); + if (sd == null) + throw new DefinitionException("Problem with use of resolve() - profile '"+element.getType().get(0).getProfile()+"' on "+element.getId()+" could not be resolved"); + childDefinitions = ProfileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep()); + } + for (ElementDefinition t : childDefinitions) { + if (tailMatches(t, expr.getName())) { + focus = t; + break; + } } } } else if (expr.getKind() == Kind.Function) { @@ -4186,7 +4190,7 @@ public class FHIRPathEngine { } if (focus == null) - throw new DefinitionException("Unable to resolve discriminator"); + throw new DefinitionException("Unable to resolve discriminator: "+expr.toString()); else if (expr.getInner() == null) return focus; else { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java index e0e325150..9da5a11ef 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java @@ -4230,11 +4230,11 @@ public class NarrativeGenerator implements INarrativeGenerator { boolean hasHistory = false; boolean hasUpdates = false; for (CapabilityStatementRestResourceComponent r : rest.getResource()) { - hasVRead = hasOp(r, TypeRestfulInteraction.VREAD); - hasPatch = hasOp(r, TypeRestfulInteraction.PATCH); - hasDelete = hasOp(r, TypeRestfulInteraction.DELETE); - hasHistory = hasOp(r, TypeRestfulInteraction.HISTORYTYPE); - hasUpdates = hasOp(r, TypeRestfulInteraction.HISTORYINSTANCE); + hasVRead = hasVRead || hasOp(r, TypeRestfulInteraction.VREAD); + hasPatch = hasPatch || hasOp(r, TypeRestfulInteraction.PATCH); + hasDelete = hasDelete || hasOp(r, TypeRestfulInteraction.DELETE); + hasHistory = hasHistory || hasOp(r, TypeRestfulInteraction.HISTORYTYPE); + hasUpdates = hasUpdates || hasOp(r, TypeRestfulInteraction.HISTORYINSTANCE); } t = x.table(null); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java index 0596b76fa..00f725187 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java @@ -837,7 +837,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat txWarning(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); return true; } - if (s.getErrorClass().isInfrastructure()) + if (s.getErrorClass() != null && s.getErrorClass().isInfrastructure()) txWarning(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); else if (s.getSeverity() == IssueSeverity.INFORMATION) txHint(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); @@ -898,8 +898,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return false; } - private void checkCodeableConcept(List errors, String path, Element focus, CodeableConcept fixed, - boolean pattern) { + private void checkCodeableConcept(List errors, String path, Element focus, CodeableConcept fixed, boolean pattern) { checkFixedValue(errors, path + ".text", focus.getNamedChild("text"), fixed.getTextElement(), "text", focus); List codings = new ArrayList(); focus.getNamedChildren("coding", codings);