diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR2.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR2.java index 743f7c10b..4bd03e308 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR2.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR2.java @@ -111,6 +111,11 @@ public class TerminologyClientR2 implements TerminologyClient { public Parameters lookupCode(Map params) throws FHIRException { return (Parameters) VersionConvertor_10_50.convertResource(client.lookupCode(params)); } + + @Override + public int getRetryCount() throws FHIRException { + return client.getRetryCount(); + } } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR3.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR3.java index 23a8e3064..531f5bc6a 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR3.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR3.java @@ -111,5 +111,10 @@ public class TerminologyClientR3 implements TerminologyClient { public Parameters lookupCode(Map params) throws FHIRException { return (Parameters) VersionConvertor_30_50.convertResource(client.lookupCode(params), false); } + + @Override + public int getRetryCount() throws FHIRException { + return client.getRetryCount(); + } } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR4.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR4.java index 0d5402f20..2b38fdfcd 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR4.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR4.java @@ -112,5 +112,10 @@ public class TerminologyClientR4 implements TerminologyClient { public Parameters lookupCode(Map params) throws FHIRException { return (Parameters) VersionConvertor_40_50.convertResource(client.lookupCode(params)); } + + @Override + public int getRetryCount() throws FHIRException { + return client.getRetryCount(); + } } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR5.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR5.java index a02a0ae94..ce6ed5d3c 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR5.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR5.java @@ -105,4 +105,9 @@ public class TerminologyClientR5 implements TerminologyClient { return this; } + @Override + public int getRetryCount() throws FHIRException { + return client.getRetryCount(); + } + } \ No newline at end of file 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 4bcbf8466..589a369ba 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 @@ -1393,12 +1393,24 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte protected String tail(String url) { - if (Utilities.noString(url)) { - return "noname"; + if (Utilities.noString(url)) { + return "noname"; + } + if (url.contains("/")) { + return url.substring(url.lastIndexOf("/")+1); + } + return url; } - if (url.contains("/")) { - return url.substring(url.lastIndexOf("/")+1); + + public int getClientRetryCount() { + return txClient == null ? 0 : txClient.getRetryCount(); } - return url; -} + + public IWorkerContext setClientRetryCount(int value) { + if (txClient != null) { + txClient.setRetryCount(value); + } + return this; + } + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java index abfc382fd..6ad572941 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java @@ -634,5 +634,6 @@ public interface IWorkerContext { public boolean hasPackage(String id, String ver); - + public int getClientRetryCount(); + public IWorkerContext setClientRetryCount(int value); } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java index da40e77f2..45caab582 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java @@ -136,8 +136,19 @@ public class QuestionnaireRenderer extends TerminologyRenderer { } if (i.hasEnableWhen()) { if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br")); - defn.getPieces().add(gen.new Piece(null, "Enable When: ", null)); - defn.getPieces().add(gen.new Piece(null, "todo", null)); + Piece p = gen.new Piece(null, "Enable When: ", null); + defn.getPieces().add(p); + if (i.getEnableWhen().size() == 0) { + XhtmlNode x = new XhtmlNode(NodeType.Element, "span"); + p.getChildren().add(x); + renderEnableWhen(x, i.getEnableWhenFirstRep()); + } else { + XhtmlNode x = new XhtmlNode(NodeType.Element, "ul"); + p.getChildren().add(x); + for (QuestionnaireItemEnableWhenComponent qi : i.getEnableWhen()) { + renderEnableWhen(x.li(), qi); + } + } } if (i.hasAnswerValueSet()) { if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br")); @@ -242,7 +253,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer { hasExt = renderLogicItem(gen, model.getRows(), q, i) || hasExt; } } - XhtmlNode xn = gen.generate(model, context.getDestDir(), 1, null); + XhtmlNode xn = gen.generate(model, context.getLocalPrefix(), 1, null); x.getChildNodes().add(xn); return hasExt; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java index f91d88d5f..55f1c5a64 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java @@ -122,6 +122,10 @@ public abstract class ResourceRenderer extends DataRenderer { } public void renderReference(ResourceWrapper rw, XhtmlNode x, Reference r, boolean allowLinks) throws UnsupportedEncodingException, IOException { + if (r == null) { + x.tx("null!"); + return; + } XhtmlNode c = null; ResourceWithReference tr = null; if (r.hasReferenceElement() && allowLinks) { @@ -132,6 +136,8 @@ public abstract class ResourceRenderer extends DataRenderer { c = x.ah(tr.getReference()); else c = x.ah(r.getReference()); + } else { + c = x.ah(r.getReference()); } } else { c = x.span(null, null); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyClient.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyClient.java index 2b32a642c..1f9f47a98 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyClient.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyClient.java @@ -48,6 +48,7 @@ public interface TerminologyClient { public Parameters validateVS(Parameters pin) throws FHIRException; public TerminologyClient setTimeout(int i) throws FHIRException; public TerminologyClient setLogger(ToolingClientLogger txLog) throws FHIRException; + public int getRetryCount() throws FHIRException; public TerminologyClient setRetryCount(int retryCount) throws FHIRException; public CapabilityStatement getCapabilitiesStatementQuick() throws FHIRException; public Parameters lookupCode(Map params) throws FHIRException; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceSorters.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceSorters.java new file mode 100644 index 000000000..81ad8906f --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceSorters.java @@ -0,0 +1,32 @@ +package org.hl7.fhir.r5.utils; + +import java.util.Comparator; + +import org.hl7.fhir.r5.model.CanonicalResource; + +public class ResourceSorters { + + public static class CanonicalResourceSortByUrl implements Comparator { + + @Override + public int compare(CanonicalResource arg0, CanonicalResource arg1) { + return arg0.getUrl().compareTo(arg1.getUrl()); + } + + } + + public static class CanonicalResourceSortByTypeId implements Comparator { + + @Override + public int compare(CanonicalResource arg0, CanonicalResource arg1) { + int ret = arg0.fhirType().compareTo(arg1.fhirType()); + if (ret == 0) { + ret = arg0.getId().compareTo(arg1.getId()); + } + return ret; + } + + } + + +} diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/FilesystemPackageCacheManager.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/FilesystemPackageCacheManager.java index f4923011a..daf717a87 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/FilesystemPackageCacheManager.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/FilesystemPackageCacheManager.java @@ -468,6 +468,9 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple } else { source = loadFromPackageServer(id, version); } + if (source == null) { + throw new FHIRException("Unable to find package "+id+"#"+version); + } return addPackageToCache(id, version == null ? source.version : version, source.stream, source.url); } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nBase.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nBase.java index 5d7a5e162..73acd1ef9 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nBase.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nBase.java @@ -14,6 +14,7 @@ public abstract class I18nBase { private Locale locale; private ResourceBundle i18nMessages; + private boolean warnAboutMissingMessages = true; public Locale getLocale() { if (Objects.nonNull(locale)) { @@ -47,8 +48,10 @@ public abstract class I18nBase { private boolean messageExistsForLocale(String message) { checkResourceBundleIsLoaded(); if (!i18nMessages.containsKey(message)) { - System.out.println("Attempting to localize message " + message + ", but no such equivalent message exists for" + - " the local " + getLocale()); + if (warnAboutMissingMessages ) { + System.out.println("Attempting to localize message " + message + ", but no such equivalent message exists for" + + " the local " + getLocale()); + } } return i18nMessages.containsKey(message); } @@ -78,4 +81,14 @@ public abstract class I18nBase { public void setValidationMessageLanguage(Locale locale) { i18nMessages = ResourceBundle.getBundle("Messages", locale); } + + public boolean isWarnAboutMissingMessages() { + return warnAboutMissingMessages; + } + + public void setWarnAboutMissingMessages(boolean warnAboutMissingMessages) { + this.warnAboutMissingMessages = warnAboutMissingMessages; + } + + } \ No newline at end of file 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 cff1e7f43..5727f10fa 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 @@ -502,4 +502,13 @@ public class I18nConstants { public static final String _HAS_CHILDREN__FOR_TYPE__IN_PROFILE__BUT_CANT_FIND_TYPE = "_has_children__for_type__in_profile__but_cant_find_type"; public static final String _HAS_NO_CHILDREN__AND_NO_TYPES_IN_PROFILE_ = "_has_no_children__and_no_types_in_profile_"; public static final String ALL_OK = "ALL_OK"; + public static final String SEARCHPARAMETER_NOTFOUND = "SEARCHPARAMETER_NOTFOUND"; + public static final String SEARCHPARAMETER_BASE_WRONG = "SEARCHPARAMETER_BASE_WRONG"; + public static final String SEARCHPARAMETER_TYPE_WRONG = "SEARCHPARAMETER_TYPE_WRONG"; + public static final String SEARCHPARAMETER_EXP_WRONG = "SEARCHPARAMETER_EXP_WRONG"; + public static final String VALUESET_NO_SYSTEM_WARNING = "VALUESET_NO_SYSTEM_WARNING"; + public static final String VALUESET_UNC_SYSTEM_WARNING = "VALUESET_UNC_SYSTEM_WARNING"; + public static final String VALUESET_UNC_SYSTEM_WARNING_VER = "VALUESET_UNC_SYSTEM_WARNING_VER"; + public static final String VALUESET_INCLUDE_INVALID_CONCEPT_CODE = "VALUESET_INCLUDE_INVALID_CONCEPT_CODE"; + public static final String VALUESET_INCLUDE_INVALID_CONCEPT_CODE_VER = "VALUESET_INCLUDE_INVALID_CONCEPT_CODE_VER"; } \ 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 7dda4b9c4..393206126 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -502,4 +502,13 @@ VALIDATION_VAL_ILLEGAL_TYPE_CONSTRAINT = Illegal constraint in profile {0} at pa EXTENSION_EXT_CONTEXT_WRONG_XVER = The extension {0} from FHIR version {3} is not allowed to be used at this point (allowed = {1}; this element is [{2}; this is a warning since contexts may be renamed between FHIR versions) SECURITY_STRING_CONTENT_ERROR = The string value contains embedded HTML tags, which are not allowed for security reasons in this context SECURITY_STRING_CONTENT_WARNING = The string value contains embedded HTML tags. Note that all inputs should be escaped when rendered to HTML as a matter of course -ALL_OK = All OK \ No newline at end of file +ALL_OK = All OK +SEARCHPARAMETER_NOTFOUND = Unable to find the base Search Parameter {0} so can't check that this SearchParameter is a proper derivation from it +SEARCHPARAMETER_BASE_WRONG = The base {1} is not listed as a base in the derivedFrom SearchParameter +SEARCHPARAMETER_TYPE_WRONG = The type {1} is different to the type {0} in the derivedFrom SearchParameter +SEARCHPARAMETER_TYPE_WRONG = The expression "{1}" is different to the expression "{0}" in the derivedFrom SearchParameter, and this likely indicates that the derivation relationship is not valid +VALUESET_NO_SYSTEM_WARNING = No System specified, so Concepts and Filters can't be checked +VALUESET_INCLUDE_INVALID_CONCEPT_CODE = The code {1} is not valid in the system {0} +VALUESET_INCLUDE_INVALID_CONCEPT_CODE_VER = The code {2} is not valid in the system {0} version {1} +VALUESET_UNC_SYSTEM_WARNING = Unknown System specified, so Concepts and Filters can't be checked +VALUESET_UNC_SYSTEM_WARNING_VER = Unknown System/Version specified, so Concepts and Filters can't be checked diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java index 5bda18774..ec8df180a 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java @@ -459,9 +459,10 @@ public class BaseValidator { * Set this parameter to false if the validation does not pass * @return Returns thePass (in other words, returns true if the rule did not fail validation) */ - protected boolean warning(List errors, IssueType type, String path, boolean thePass, String msg) { + protected boolean warning(List errors, IssueType type, String path, boolean thePass, String msg, Object... theMessageArguments) { if (!thePass) { - addValidationMessage(errors, type, -1, -1, path, msg, IssueSeverity.WARNING, null); + String message = context.formatMessage(msg, theMessageArguments); + addValidationMessage(errors, type, -1, -1, path, message, IssueSeverity.WARNING, null); } return thePass; } @@ -473,7 +474,7 @@ public class BaseValidator { * Set this parameter to false if the validation does not pass * @return Returns thePass (in other words, returns true if the rule did not fail validation) */ - protected boolean warning(List errors, IssueType type, String path, boolean thePass, String msg, String html) { + protected boolean warningHtml(List errors, IssueType type, String path, boolean thePass, String msg, String html) { if (!thePass) { addValidationMessage(errors, type, path, msg, html, IssueSeverity.WARNING, null); } @@ -487,7 +488,7 @@ public class BaseValidator { * Set this parameter to false if the validation does not pass * @return Returns thePass (in other words, returns true if the rule did not fail validation) */ - protected boolean warning(List errors, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) { + protected boolean warningHtml(List errors, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) { if (!thePass) { String nmsg = context.formatMessage(msg, theMessageArguments); addValidationMessage(errors, type, path, nmsg, html, IssueSeverity.WARNING, msg); 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 f5a2c90bc..b11bcde24 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 @@ -140,6 +140,8 @@ import org.hl7.fhir.validation.instance.type.BundleValidator; import org.hl7.fhir.validation.instance.type.CodeSystemValidator; import org.hl7.fhir.validation.instance.type.MeasureValidator; import org.hl7.fhir.validation.instance.type.QuestionnaireValidator; +import org.hl7.fhir.validation.instance.type.SearchParameterValidator; +import org.hl7.fhir.validation.instance.type.ValueSetValidator; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities.DecimalStatus; @@ -3464,6 +3466,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat validateCapabilityStatement(errors, element, stack); } else if (element.getType().equals("CodeSystem")) { new CodeSystemValidator(context, timeTracker).validateCodeSystem(errors, element, stack); + } else if (element.getType().equals("SearchParameter")) { + new SearchParameterValidator(context, timeTracker).validateSearchParameter(errors, element, stack); + } else if (element.getType().equals("ValueSet")) { + new ValueSetValidator(context, timeTracker).validateValueSet(errors, element, stack); } } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/SearchParameterValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/SearchParameterValidator.java new file mode 100644 index 000000000..129bf5f3d --- /dev/null +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/SearchParameterValidator.java @@ -0,0 +1,46 @@ +package org.hl7.fhir.validation.instance.type; + +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.SearchParameter; +import org.hl7.fhir.r5.model.ValueSet; +import org.hl7.fhir.utilities.Utilities; +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.validation.BaseValidator; +import org.hl7.fhir.validation.TimeTracker; +import org.hl7.fhir.validation.instance.utils.NodeStack; + +public class SearchParameterValidator extends BaseValidator { + + public SearchParameterValidator(IWorkerContext context, TimeTracker timeTracker) { + super(context); + source = Source.InstanceValidator; + this.timeTracker = timeTracker; + } + + public void validateSearchParameter(List errors, Element cs, NodeStack stack) { + String url = cs.getNamedChildValue("url"); + String master = cs.getNamedChildValue("derivedFrom"); + + if (!Utilities.noString(master)) { + SearchParameter sp = context.fetchResource(SearchParameter.class, master); + if (warning(errors, IssueType.BUSINESSRULE,stack.getLiteralPath(), sp != null, I18nConstants.SEARCHPARAMETER_NOTFOUND, master)) { + // base must be in the master list of base + List bl = cs.getChildren("base"); + for (Element b : bl) { + rule(errors, IssueType.BUSINESSRULE,stack.getLiteralPath(), sp.hasBase(b.primitiveValue()), I18nConstants.SEARCHPARAMETER_BASE_WRONG, master, b.primitiveValue()); + } + rule(errors, IssueType.BUSINESSRULE,stack.getLiteralPath(), !cs.hasChild("type") || sp.getType().toCode().equals(cs.getNamedChildValue("type")), I18nConstants.SEARCHPARAMETER_TYPE_WRONG, master, sp.getType().toCode(), cs.getNamedChildValue("type")); + warning(errors, IssueType.BUSINESSRULE,stack.getLiteralPath(), !cs.hasChild("expression") || sp.getExpression().equals(cs.getNamedChildValue("expression")), I18nConstants.SEARCHPARAMETER_EXP_WRONG, master, sp.getExpression(), cs.getNamedChildValue("expression")); + // todo: cjeck compositions + } + } + } + +} diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java new file mode 100644 index 000000000..42938d1fd --- /dev/null +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java @@ -0,0 +1,107 @@ +package org.hl7.fhir.validation.instance.type; + +import java.util.List; + +import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; +import org.hl7.fhir.r5.elementmodel.Element; +import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.VersionUtilities; +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; + +public class ValueSetValidator extends BaseValidator { + + public ValueSetValidator(IWorkerContext context, TimeTracker timeTracker) { + super(context); + source = Source.InstanceValidator; + this.timeTracker = timeTracker; + } + + public void validateValueSet(List errors, Element vs, NodeStack stack) { + if (!VersionUtilities.isR2Ver(context.getVersion())) { + List composes = vs.getChildrenByName("compose"); + int cc = 0; + for (Element compose : composes) { + validateValueSetCompose(errors, compose, stack.push(compose, cc, null, null)); + cc++; + } + } + } + + private void validateValueSetCompose(List errors, Element compose, NodeStack stack) { + List includes = compose.getChildrenByName("include"); + int ci = 0; + for (Element include : includes) { + validateValueSetInclude(errors, include, stack.push(include, ci, null, null)); + ci++; + } + List excludes = compose.getChildrenByName("exclude"); + int ce = 0; + for (Element exclude : excludes) { + validateValueSetInclude(errors, exclude, stack.push(exclude, ce, null, null)); + ce++; + } + } + + private void validateValueSetInclude(List errors, Element include, NodeStack stack) { + String system = include.getChildValue("system"); + String version = include.getChildValue("version"); + boolean systemOk = true; + List concepts = include.getChildrenByName("concept"); + List filters = include.getChildrenByName("filter"); + if (!Utilities.noString(system)) { + int cc = 0; + for (Element concept : concepts) { + if (systemOk && !validateValueSetIncludeConcept(errors, concept, stack.push(concept, cc, null, null), system, version)) { + systemOk = false; + } + cc++; + } + int cf = 0; + for (Element filter : filters) { + if (systemOk && !validateValueSetIncludeFilter(errors, include, stack.push(filter, cf, null, null), system, version)) { + systemOk = false; + } + cf++; + } + warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), systemOk, version == null ? I18nConstants.VALUESET_UNC_SYSTEM_WARNING : I18nConstants.VALUESET_UNC_SYSTEM_WARNING_VER); + } else { + warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), filters.size() == 0 && concepts.size() == 0, I18nConstants.VALUESET_NO_SYSTEM_WARNING); + } + } + + private boolean validateValueSetIncludeConcept(List errors, Element concept, NodeStack stack, String system, String version) { + String code = concept.getChildValue("code"); + if (version == null) { + ValidationResult vv = context.validateCode(ValidationOptions.defaults(), new Coding(system, code, null), null); + if (vv.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) { + return false; + } else { + boolean ok = vv.isOk(); + warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), ok, I18nConstants.VALUESET_INCLUDE_INVALID_CONCEPT_CODE, system, code); + } + } else { + ValidationResult vv = context.validateCode(ValidationOptions.defaults(), new Coding(system, code, null).setVersion(version), null); + if (vv.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) { + return false; + } else { + boolean ok = vv.isOk(); + warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), ok, I18nConstants.VALUESET_INCLUDE_INVALID_CONCEPT_CODE_VER, system, version, code); + } + } + return true; + } + + private boolean validateValueSetIncludeFilter(List errors, Element include, NodeStack push, String system, String version) { + return true; + } +} diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java index 7371a687d..1a942e31a 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java @@ -92,6 +92,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe private static JsonObject manifest; private JsonObject content; private String version; + private String name; private static final String DEF_TX = "http://tx.fhir.org"; private static Map ve = new HashMap<>(); @@ -102,6 +103,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe public void test(String name, JsonObject content) throws Exception { long setup = System.nanoTime(); this.content = content; + this.name = name; System.out.println("---- " + name + " ----------------------------------------------------------------"); System.out.println("** Core: "); String txLog = null; @@ -142,6 +144,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe String testCaseContent = TestingUtilities.loadTestResource("validator", name); InstanceValidator val = vCurr.getValidator(); + val.getContext().setClientRetryCount(4); val.setDebug(false); if (content.has("allowed-extension-domain")) val.getExtensionDomains().add(content.get("allowed-extension-domain").getAsString()); @@ -337,11 +340,11 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe } } if (!TestingUtilities.context(version).isNoTerminologyServer() || !focus.has("tx-dependent")) { - Assertions.assertEquals(java.get("errorCount").getAsInt(), ec, "Expected " + Integer.toString(java.get("errorCount").getAsInt()) + " errors, but found " + Integer.toString(ec) + "."); + Assertions.assertEquals(java.get("errorCount").getAsInt(), ec, "Test "+name+": Expected " + Integer.toString(java.get("errorCount").getAsInt()) + " errors, but found " + Integer.toString(ec) + "."); if (java.has("warningCount")) - Assertions.assertEquals(java.get("warningCount").getAsInt(), wc, "Expected " + Integer.toString(java.get("warningCount").getAsInt()) + " warnings, but found " + Integer.toString(wc) + "."); + Assertions.assertEquals(java.get("warningCount").getAsInt(), wc, "Test "+name+": Expected " + Integer.toString(java.get("warningCount").getAsInt()) + " warnings, but found " + Integer.toString(wc) + "."); if (java.has("infoCount")) - Assertions.assertEquals(java.get("infoCount").getAsInt(), hc, "Expected " + Integer.toString(java.get("infoCount").getAsInt()) + " hints, but found " + Integer.toString(hc) + "."); + Assertions.assertEquals(java.get("infoCount").getAsInt(), hc, "Test "+name+": Expected " + Integer.toString(java.get("infoCount").getAsInt()) + " hints, but found " + Integer.toString(hc) + "."); } if (java.has("error-locations")) { JsonArray el = java.getAsJsonArray("error-locations"); diff --git a/pom.xml b/pom.xml index f483679d6..a6e97bf16 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ 5.0.0 - 1.1.21 + 1.1.22-SNAPSHOT 5.6.2 3.0.0-M4 0.8.5