diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java index 047f8143c..b7d506839 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java @@ -750,7 +750,9 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte // 3rd pass: hit the server for (CodingValidationRequest t : codes) { t.setCacheToken(txCache != null ? txCache.generateValidationToken(options, t.getCoding(), vs) : null); - codeSystemsUsed.add(t.getCoding().getSystem()); + if (t.getCoding().hasSystem()) { + codeSystemsUsed.add(t.getCoding().getSystem()); + } if (txCache != null) { t.setResult(txCache.getValidation(t.getCacheToken())); } @@ -846,7 +848,9 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte options = ValidationOptions.defaults(); } - codeSystemsUsed.add(code.getSystem()); + if (code.hasSystem()) { + codeSystemsUsed.add(code.getSystem()); + } CacheToken cacheToken = txCache != null ? txCache.generateValidationToken(options, code, vs) : null; ValidationResult res = null; if (txCache != null) { @@ -922,7 +926,9 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte return res; } for (Coding c : code.getCoding()) { - codeSystemsUsed.add(c.getSystem()); + if (c.hasSystem()) { + codeSystemsUsed.add(c.getSystem()); + } } if (options.isUseClient()) { @@ -1134,6 +1140,10 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte return noTerminologyServer; } + public void setNoTerminologyServer(boolean noTerminologyServer) { + this.noTerminologyServer = noTerminologyServer; + } + public String getName() { return name; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/JsonParser.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/JsonParser.java index 1619e33f2..f635ac98d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/JsonParser.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/JsonParser.java @@ -76,15 +76,19 @@ public class JsonParser extends ParserBase { private Map map; private boolean allowComments; - private FHIRPathEngine fpe; private ProfileUtilities profileUtilities; - public JsonParser(IWorkerContext context) { - super(context); + public JsonParser(IWorkerContext context, ProfileUtilities utilities) { + super(context); - this.fpe = new FHIRPathEngine(this.context); - this.profileUtilities = new ProfileUtilities(this.context, null, null, this.fpe); - } + this.profileUtilities = utilities; + } + + public JsonParser(IWorkerContext context) { + super(context); + + this.profileUtilities = new ProfileUtilities(this.context, null, null, new FHIRPathEngine(context)); + } public Element parse(String source, String type) throws Exception { JsonObject obj = (JsonObject) new com.google.gson.JsonParser().parse(source); 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 af62c7fb4..f20a15d02 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 @@ -152,6 +152,9 @@ public class ValueSetCheckerSimple implements ValueSetChecker { throw new FHIRException(warningMessage); } } + if (cs != null && cs.hasSupplements()) { + return new ValidationResult(IssueSeverity.ERROR, context.formatMessage(I18nConstants.CODESYSTEM_CS_NO_SUPPLEMENT, cs.getUrl())); + } if (cs!=null && cs.getContent() != CodeSystemContentMode.COMPLETE) { warningMessage = "Resolved system "+system+", but the definition is not complete"; if (!inExpansion && cs.getContent() != CodeSystemContentMode.FRAGMENT) { // we're going to give it a go if it's a fragment 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 754c33f9d..f857c5ef5 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 @@ -304,7 +304,6 @@ public class FHIRPathEngine { public ValueSet resolveValueSet(Object appContext, String url); } - /** * @param worker - used when validating paths (@check), and used doing value set membership when executing tests (once that's defined) */ diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java index 4c29d74a5..fb6690293 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java @@ -50,6 +50,9 @@ public class I18nConstants { public static final String CODESYSTEM_CS_VS_INVALID = "CodeSystem_CS_VS_Invalid"; public static final String CODESYSTEM_CS_VS_EXP_MISMATCH = "CODESYSTEM_CS_VS_EXP_MISMATCH"; public static final String CODESYSTEM_CS_VS_WRONGSYSTEM = "CodeSystem_CS_VS_WrongSystem"; + public static final String CODESYSTEM_CS_NO_SUPPLEMENT = "CODESYSTEM_CS_NO_SUPPLEMENT"; + public static final String CODESYSTEM_CS_SUPP_CANT_CHECK = "CODESYSTEM_CS_SUPP_CANT_CHECK"; + public static final String CODESYSTEM_CS_SUPP_INVALID_CODE = "CODESYSTEM_CS_SUPP_INVALID_CODE"; public static final String CODE_FOUND_IN_EXPANSION_HOWEVER_ = "Code_found_in_expansion_however_"; public static final String CODING_HAS_NO_SYSTEM__CANNOT_VALIDATE = "Coding_has_no_system__cannot_validate"; public static final String CONTAINED_RESOURCE_DOES_NOT_APPEAR_TO_BE_A_FHIR_RESOURCE_UNKNOWN_NAME_ = "Contained_resource_does_not_appear_to_be_a_FHIR_resource_unknown_name_"; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationMessage.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationMessage.java index 74990d315..3213c1a33 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationMessage.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationMessage.java @@ -3,19 +3,19 @@ package org.hl7.fhir.utilities.validation; /* Copyright (c) 2011+, HL7, Inc. All rights reserved. - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to + * Neither the name of HL7 nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. @@ -26,7 +26,7 @@ package org.hl7.fhir.utilities.validation; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - + */ @@ -127,6 +127,7 @@ public class ValidationMessage implements Comparator, Compara case ERROR: return "error"; case WARNING: return "warning"; case INFORMATION: return "information"; + case NULL: return null; default: return "?"; } } @@ -136,6 +137,7 @@ public class ValidationMessage implements Comparator, Compara case ERROR: return "http://hl7.org/fhir/issue-severity"; case WARNING: return "http://hl7.org/fhir/issue-severity"; case INFORMATION: return "http://hl7.org/fhir/issue-severity"; + case NULL: return null; default: return "?"; } } @@ -145,6 +147,7 @@ public class ValidationMessage implements Comparator, Compara case ERROR: return "The issue is sufficiently important to cause the action to fail."; case WARNING: return "The issue is not important enough to cause the action to fail, but may cause it to be performed suboptimally or in a way that is not as desired."; case INFORMATION: return "The issue has no relation to the degree of success of the action."; + case NULL: return null; default: return "?"; } } @@ -154,6 +157,7 @@ public class ValidationMessage implements Comparator, Compara case ERROR: return "Error"; case WARNING: return "Warning"; case INFORMATION: return "Information"; + case NULL: return null; default: return "?"; } } @@ -380,6 +384,7 @@ public class ValidationMessage implements Comparator, Compara case TIMEOUT: return "timeout"; case THROTTLED: return "throttled"; case INFORMATIONAL: return "informational"; + case NULL: return null; default: return "?"; } } @@ -414,6 +419,7 @@ public class ValidationMessage implements Comparator, Compara case TIMEOUT: return "http://hl7.org/fhir/issue-type"; case THROTTLED: return "http://hl7.org/fhir/issue-type"; case INFORMATIONAL: return "http://hl7.org/fhir/issue-type"; + case NULL: return null; default: return "?"; } } @@ -448,6 +454,7 @@ public class ValidationMessage implements Comparator, Compara case TIMEOUT: return "An internal timeout has occurred."; case THROTTLED: return "The system is not prepared to handle this request due to load management."; case INFORMATIONAL: return "A message unrelated to the processing success of the completed operation (examples of the latter include things like reminders of password expiry, system maintenance times, etc.)."; + case NULL: return null; default: return "?"; } } @@ -482,6 +489,7 @@ public class ValidationMessage implements Comparator, Compara case TIMEOUT: return "Timeout"; case THROTTLED: return "Throttled"; case INFORMATIONAL: return "Informational Note"; + case NULL: return null; default: return "?"; } } @@ -692,7 +700,7 @@ public class ValidationMessage implements Comparator, Compara public String getDisplay() { return level + ": " + (location==null || location.isEmpty() ? "" : (location + ": ")) + message; } - + /** * Returns a representation of this ValidationMessage suitable for logging. The values of * most of the internal fields are included, so this may not be suitable for display to @@ -781,5 +789,5 @@ public class ValidationMessage implements Comparator, Compara this.signpost = signpost; } - + } \ No newline at end of file diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index f4dde798c..d8b40f7af 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -106,8 +106,8 @@ Questionnaire_Q_EnableWhen_Self = Target for this question enableWhen can''t ref Reference_REF_Aggregation = Reference is {0} which isn''t supported by the specified aggregation mode(s) for the reference ({1}) Reference_REF_BadTargetType = Invalid Resource target type. Found {0}, but expected one of ({1}) Reference_REF_BadTargetType2 = The type ''{0}'' implied by the reference URL {1} is not a valid Target for this element (must be one of {2}) -Reference_REF_CantMatchChoice = Unable to find matching profile for {0} among choices: {1} -Reference_REF_CantMatchType = Unable to find matching profile for {0} (by type) among choices: {1} +Reference_REF_CantMatchChoice = Unable to find a match for profile {0} among choices: {1} +Reference_REF_CantMatchType = Unable to find a match for profile {0} (by type) among choices: {1} Reference_REF_CantResolve = Unable to resolve resource ''{0}'' Reference_REF_CantResolveProfile = Unable to resolve the profile reference ''{0}'' Reference_REF_Format1 = Relative URLs must be of the format [ResourceName]/[id], or a search URL is allowed ([type]?parameters. Encountered {0}) @@ -217,7 +217,7 @@ Validation_VAL_Profile_NoCheckMax = {2}: Unable to check max allowed ({1}) due t Validation_VAL_Profile_NoCheckMin = {2}: Unable to check minimum required ({1}) due to lack of slicing validation (from {0}) Validation_VAL_Profile_MultipleMatches = Found multiple matching profiles among choices: {0} Validation_VAL_Profile_NoDefinition = No definition found for resource type ''{0}'' -Validation_VAL_Profile_NoMatch = Unable to find matching profile among choices: {0} +Validation_VAL_Profile_NoMatch = Unable to find a match for the specified profile among choices: {0} Validation_VAL_Profile_NoSnapshot = StructureDefinition has no snapshot - validation is against the snapshot, so it must be provided Validation_VAL_Profile_NoType = The type of element {0} is not known, which is illegal. Valid types at this point are {1} Validation_VAL_Profile_NotAllowed = This element is not allowed by the profile {0} @@ -636,3 +636,6 @@ DISCRIMINATOR_BAD_PATH = Error processing path expression for disciminator: {0} SLICING_CANNOT_BE_EVALUATED = Slicing cannot be evaluated: {0} TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE = Canonical URL ''{0}'' does not resolve TYPE_SPECIFIC_CHECKS_DT_CANONICAL_TYPE = Canonical URL ''{0}'' refers to a resource that has the wrong type. Found {1} expecting one of {2} +CODESYSTEM_CS_NO_SUPPLEMENT = CodeSystem {0} is a supplement, so can't be used as a value in Coding.system +CODESYSTEM_CS_SUPP_CANT_CHECK = CodeSystem {0} cannot be found, so can't check if concepts are valid +CODESYSTEM_CS_SUPP_INVALID_CODE = The code ''{1}'' is not declared in the base CodeSystem {0} so is not valid in the supplement diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java index a46d9df04..ab1766fe0 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java @@ -834,6 +834,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst context.setTlogging(false); if (url == null) { context.setCanRunWithoutTerminology(true); + context.setNoTerminologyServer(true); return "n/a: No Terminology Server"; } else { try { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java index c2ce5fc77..a2be458e8 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java @@ -129,10 +129,10 @@ public class Params { cliContext.getBundleValidationRules().add(new BundleValidationRule(r, p)); } else if (args[i].equals(QUESTIONNAIRE)) { if (i + 1 == args.length) - throw new Error("Specified -questionnaire without indicating questionnaire file"); + throw new Error("Specified -questionnaire without indicating questionnaire mode"); else { String q = args[++i]; - cliContext.setQuestionnaireMode(QuestionnaireMode.valueOf(q)); + cliContext.setQuestionnaireMode(QuestionnaireMode.fromCode(q)); } } else if (args[i].equals(NATIVE)) { cliContext.setDoNative(true); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/ProfileLoader.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/ProfileLoader.java index e1587c918..0a4364cec 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/ProfileLoader.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/ProfileLoader.java @@ -27,6 +27,7 @@ public class ProfileLoader { try { URL url = new URL(src + "?nocache=" + System.currentTimeMillis()); URLConnection c = url.openConnection(); + return IOUtils.toByteArray(c.getInputStream()); } catch (Exception e) { throw new FHIRException("Unable to find definitions at URL '" + src + "': " + e.getMessage(), e); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index 2ebae0d89..01e66f438 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -660,7 +660,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat @Override public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, JsonObject object, List profiles) throws FHIRException { - JsonParser parser = new JsonParser(context); + JsonParser parser = new JsonParser(context, new ProfileUtilities(context, null, null, fpe)); parser.setupValidation(ValidationPolicy.EVERYTHING, errors); long t = System.nanoTime(); Element e = parser.parse(object); @@ -2158,6 +2158,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return true; } } + if (tr.getTargetProfile().isEmpty()) { + return true; + } } return false; } @@ -2400,7 +2403,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { 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, stack); - else if (!noExtensibleWarnings) + else if (!noExtensibleWarnings && !isOkExtension(value, vs)) txWarningForLaterRemoval(element, errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_17, value, describeReference(binding.getValueSet()), vs.getUrl(), getErrorMessage(vr.getMessage())); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { @@ -2413,6 +2416,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !type.equals("code"), I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE2); } + private boolean isOkExtension(String value, ValueSet vs) { + if ("http://hl7.org/fhir/ValueSet/defined-types".equals(vs.getUrl())) { + return value.startsWith("http://hl7.org/fhirpath/System."); + } + return false; + } + private void checkQuantity(List errors, String path, Element focus, Quantity fixed, String fixedSource, boolean pattern) { checkFixedValue(errors, path + ".value", focus.getNamedChild("value"), fixed.getValueElement(), fixedSource, "value", focus, pattern); checkFixedValue(errors, path + ".comparator", focus.getNamedChild("comparator"), fixed.getComparatorElement(), fixedSource, "comparator", focus, pattern); @@ -3166,36 +3176,53 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat // really, there should only be one level for this (contained resources cannot contain // contained resources), but we'll leave that to some other code to worry about boolean wasContained = false; - while (stack != null && stack.getElement() != null) { - if (stack.getElement().getProperty().isResource()) { + NodeStack nstack = stack; + while (nstack != null && nstack.getElement() != null) { + if (nstack.getElement().getProperty().isResource()) { // ok, we'll try to find the contained reference - if (ref.equals("#") && stack.getElement().getSpecial() != SpecialElement.CONTAINED && wasContained) { + if (ref.equals("#") && nstack.getElement().getSpecial() != SpecialElement.CONTAINED && wasContained) { ResolvedReference rr = new ResolvedReference(); - rr.setResource(stack.getElement()); - rr.setFocus(stack.getElement()); + rr.setResource(nstack.getElement()); + rr.setFocus(nstack.getElement()); rr.setExternal(false); - rr.setStack(stack.push(stack.getElement(), -1, stack.getElement().getProperty().getDefinition(), stack.getElement().getProperty().getDefinition())); - rr.getStack().qualifyPath(".ofType("+stack.getElement().fhirType()+")"); + rr.setStack(nstack.push(nstack.getElement(), -1, nstack.getElement().getProperty().getDefinition(), nstack.getElement().getProperty().getDefinition())); + rr.getStack().qualifyPath(".ofType("+nstack.getElement().fhirType()+")"); return rr; } - if (stack.getElement().getSpecial() == SpecialElement.CONTAINED) { + if (nstack.getElement().getSpecial() == SpecialElement.CONTAINED) { wasContained = true; } - IndexedElement res = getContainedById(stack.getElement(), ref.substring(1)); + IndexedElement res = getContainedById(nstack.getElement(), ref.substring(1)); if (res != null) { ResolvedReference rr = new ResolvedReference(); - rr.setResource(stack.getElement()); + rr.setResource(nstack.getElement()); rr.setFocus(res.getMatch()); rr.setExternal(false); - rr.setStack(stack.push(res.getMatch(), res.getIndex(), res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition())); - rr.getStack().qualifyPath(".ofType("+stack.getElement().fhirType()+")"); + rr.setStack(nstack.push(res.getMatch(), res.getIndex(), res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition())); + rr.getStack().qualifyPath(".ofType("+nstack.getElement().fhirType()+")"); return rr; } } - if (stack.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY || stack.getElement().getSpecial() == SpecialElement.PARAMETER) { + if (nstack.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY || nstack.getElement().getSpecial() == SpecialElement.PARAMETER) { return null; // we don't try to resolve contained references across this boundary } - stack = stack.getParent(); + nstack = nstack.getParent(); + } + // try again, and work up the element parent list + if (ref.equals("#")) { + Element e = stack.getElement(); + while (e != null) { + if (e.getProperty().isResource() && (e.getSpecial() != SpecialElement.CONTAINED)) { + ResolvedReference rr = new ResolvedReference(); + rr.setResource(e); + rr.setFocus(e); + rr.setExternal(false); + rr.setStack(stack.push(e, -1, e.getProperty().getDefinition(), e.getProperty().getDefinition())); + rr.getStack().qualifyPath(".ofType("+e.fhirType()+")"); + return rr; + } + e = e.getParentForValidator(); + } } return null; } else { @@ -4007,7 +4034,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else if (element.getType().equals("CapabilityStatement")) { validateCapabilityStatement(errors, element, stack); } else if (element.getType().equals("CodeSystem")) { - new CodeSystemValidator(context, timeTracker, xverManager).validateCodeSystem(errors, element, stack); + new CodeSystemValidator(context, timeTracker, xverManager).validateCodeSystem(errors, element, stack, new ValidationOptions(stack.getWorkingLang())); } else if (element.getType().equals("SearchParameter")) { new SearchParameterValidator(context, timeTracker, fpe, xverManager).validateSearchParameter(errors, element, stack); } else if (element.getType().equals("StructureDefinition")) { @@ -4951,15 +4978,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (inv.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-bestpractice") && ToolingExtensions.readBooleanExtension(inv, "http://hl7.org/fhir/StructureDefinition/elementdefinition-bestpractice")) { if (bpWarnings == BestPracticeWarningLevel.Hint) - hint(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); + hint(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + (Utilities.noString(msg) ? "failed" : msg)); else if (bpWarnings == BestPracticeWarningLevel.Warning) - warning(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); + warning(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + (Utilities.noString(msg) ? "failed" : msg)); else if (bpWarnings == BestPracticeWarningLevel.Error) - rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); + rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + (Utilities.noString(msg) ? "failed" : msg)); } else if (inv.getSeverity() == ConstraintSeverity.ERROR) { - rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); + rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + (Utilities.noString(msg) ? "failed" : msg)); } else if (inv.getSeverity() == ConstraintSeverity.WARNING) { - warning(errors, IssueType.INVARIANT, element.line(), element.line(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); + warning(errors, IssueType.INVARIANT, element.line(), element.line(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + (Utilities.noString(msg) ? "failed" : msg)); } } } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/CodeSystemValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/CodeSystemValidator.java index 869322d58..48974d24c 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/CodeSystemValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/CodeSystemValidator.java @@ -5,6 +5,7 @@ import java.util.List; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.elementmodel.Element; +import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.utilities.Utilities; @@ -12,10 +13,13 @@ import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.utilities.validation.ValidationMessage.Source; +import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.validation.BaseValidator; import org.hl7.fhir.validation.TimeTracker; import org.hl7.fhir.validation.instance.utils.NodeStack; +import ca.uhn.fhir.validation.ValidationResult; + public class CodeSystemValidator extends BaseValidator { public CodeSystemValidator(IWorkerContext context, TimeTracker timeTracker, XVerExtensionManager xverManager) { @@ -24,7 +28,7 @@ public class CodeSystemValidator extends BaseValidator { this.timeTracker = timeTracker; } - public void validateCodeSystem(List errors, Element cs, NodeStack stack) { + public void validateCodeSystem(List errors, Element cs, NodeStack stack, ValidationOptions options) { String url = cs.getNamedChildValue("url"); String content = cs.getNamedChildValue("content"); @@ -52,6 +56,31 @@ public class CodeSystemValidator extends BaseValidator { } } } // todo... try getting the value set the other way... + + String supp = cs.getNamedChildValue("supplements"); + if (supp != null) { + if (context.supportsSystem(supp)) { + List concepts = cs.getChildrenByName("concept"); + int ce = 0; + for (Element concept : concepts) { + validateSupplementConcept(errors, concept, stack.push(concept, ce, null, null), supp, options); + ce++; + } + } else { + if (cs.hasChildren("concept")) { + warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), false, I18nConstants.CODESYSTEM_CS_SUPP_CANT_CHECK, supp); + } + } + } + } + + private void validateSupplementConcept(List errors, Element concept, NodeStack stack, String supp, ValidationOptions options) { + String code = concept.getChildValue("code"); + if (!Utilities.noString(code)) { + org.hl7.fhir.r5.context.IWorkerContext.ValidationResult res = context.validateCode(options, supp, code, null); + rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), res.isOk(), I18nConstants.CODESYSTEM_CS_SUPP_INVALID_CODE, supp, code); + } + } private int countConcepts(Element cs) { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ValidatorHostContext.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ValidatorHostContext.java index dcbe1e429..507951d7c 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ValidatorHostContext.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ValidatorHostContext.java @@ -86,7 +86,9 @@ public class ValidatorHostContext { } public void sliceNotes(String url, List record) { + if (sliceRecords != null) { sliceRecords.put(url, record); + } } public ValidatorHostContext forContained(Element element) {