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 5c2ceb57d..974f93f88 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 @@ -156,6 +156,7 @@ import org.hl7.fhir.r5.model.TimeType; import org.hl7.fhir.r5.model.Timing; import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.UrlType; +import org.hl7.fhir.r5.model.UsageContext; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.renderers.DataRenderer; @@ -494,18 +495,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat Element e = new ObjectConverter(context).convert((Resource) item); setParents(e); self.validateResource(new ValidationContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, - mode, false); + mode, false, false); } catch (IOException e1) { throw new FHIRException(e1); } } else if (item instanceof Element) { Element e = (Element) item; if (e.getSpecial() == SpecialElement.CONTAINED) { - self.validateResource(new ValidationContext(ctxt.getAppContext(), e, ctxt.getRootResource(), ctxt.getGroupingResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode, false); + self.validateResource(new ValidationContext(ctxt.getAppContext(), e, ctxt.getRootResource(), ctxt.getGroupingResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode, false, false); } else if (e.getSpecial() != null) { - self.validateResource(new ValidationContext(ctxt.getAppContext(), e, e, ctxt.getRootResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode, false); + self.validateResource(new ValidationContext(ctxt.getAppContext(), e, e, ctxt.getRootResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode, false, false); } else { - self.validateResource(new ValidationContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode, false); + self.validateResource(new ValidationContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode, false, false); } } else throw new NotImplementedException(context.formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESCONFORMSTOPROFILE_WHEN_ITEM_IS_NOT_AN_ELEMENT)); @@ -603,7 +604,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private List bundleValidationRules = new ArrayList<>(); private boolean validateValueSetCodesOnTxServer = true; private QuestionnaireMode questionnaireMode; - private ValidationOptions baseOptions = new ValidationOptions(FhirPublication.R5); private Map crLookups = new HashMap<>(); private boolean logProgress; private CodingsObserver codingObserver; @@ -1002,7 +1002,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat long t = System.nanoTime(); NodeStack stack = new NodeStack(context, path, element, validationLanguage); if (profiles == null || profiles.isEmpty()) { - validateResource(new ValidationContext(appContext, element), errors, element, element, null, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.BaseDefinition), false); + validateResource(new ValidationContext(appContext, element), errors, element, element, null, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.BaseDefinition), false, false); } else { int i = 0; while (i < profiles.size()) { @@ -1020,7 +1020,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat i++; } for (StructureDefinition defn : profiles) { - validateResource(new ValidationContext(appContext, element), errors, element, element, defn, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.ConfigProfile), false); + validateResource(new ValidationContext(appContext, element), errors, element, element, defn, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.ConfigProfile), false, false); } } if (hintAboutNonMustSupport) { @@ -2399,7 +2399,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else if (sd.getType().equals(resource.fhirType())) { List valerrors = new ArrayList(); ValidationMode mode = new ValidationMode(ValidationReason.Expression, ProfileSource.FromExpression); - validateResource(new ValidationContext(appContext, resource), valerrors, resource, resource, sd, IdStatus.OPTIONAL, new NodeStack(context, null, resource, validationLanguage), null, mode, false); + validateResource(new ValidationContext(appContext, resource), valerrors, resource, resource, sd, IdStatus.OPTIONAL, new NodeStack(context, null, resource, validationLanguage), null, mode, false, false); boolean ok = true; List record = new ArrayList<>(); for (ValidationMessage v : valerrors) { @@ -3070,11 +3070,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat public boolean validateReference(ValidationContext valContext, List errors, String path, String type, ElementDefinition context, Element e, String url) { boolean ok = true; + if (url.startsWith("#")) { + valContext.getInternalRefs().add(url.substring(1)); + } // now, do we check the URI target? if (fetcher != null && !type.equals("uuid")) { - if (url.startsWith("#")) { - valContext.getInternalRefs().add(url.substring(1)); - } boolean found; try { found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com")) || url.contains("acme.org")) /* || (url.startsWith("http://hl7.org/fhir/tools")) */ || @@ -3993,7 +3993,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat for (StructureDefinition pr : profiles) { List profileErrors = new ArrayList(); validateResource(we.valContext(valContext, pr), profileErrors, we.getResource(), we.getFocus(), pr, - IdStatus.OPTIONAL, we.getStack().resetIds(), pct, vmode.withReason(ValidationReason.MatchingSlice), true); + IdStatus.OPTIONAL, we.getStack().resetIds(), pct, vmode.withReason(ValidationReason.MatchingSlice), true, false); if (!hasErrors(profileErrors)) { goodCount++; goodProfiles.put(pr, profileErrors); @@ -5379,7 +5379,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } // checkSpecials = we're only going to run these tests if we are actually validating this content (as opposed to we looked it up) - private boolean start(ValidationContext valContext, List errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, PercentageTracker pct, ValidationMode mode) throws FHIRException { + private boolean start(ValidationContext valContext, List errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, PercentageTracker pct, ValidationMode mode, boolean fromContained) throws FHIRException { boolean ok = !hasErrors(errors); checkLang(resource, stack); @@ -5399,7 +5399,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } resolveBundleReferences(element, new ArrayList()); } - ok = startInner(valContext, errors, resource, element, defn, stack, valContext.isCheckSpecials(), pct, mode) && ok; + ok = startInner(valContext, errors, resource, element, defn, stack, valContext.isCheckSpecials(), pct, mode, fromContained) && ok; if (pctOwned) { pct.done(); } @@ -5418,7 +5418,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (pctOwned) { pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sdi.getUrl(), logProgress); } - ok = startInner(valContext, errors, resource, element, sdi, stack, false, pct, mode.withSource(ProfileSource.ProfileDependency)) && ok; + ok = startInner(valContext, errors, resource, element, sdi, stack, false, pct, mode.withSource(ProfileSource.ProfileDependency), fromContained) && ok; if (pctOwned) { pct.done(); } @@ -5466,7 +5466,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (pctOwned) { pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sd.getUrl(), logProgress); } - ok = startInner(valContext, errors, resource, element, sd, stack, false, pct, mode.withSource(ProfileSource.MetaProfile)) && ok; + ok = startInner(valContext, errors, resource, element, sd, stack, false, pct, mode.withSource(ProfileSource.MetaProfile), fromContained) && ok; if (pctOwned) { pct.done(); } @@ -5483,7 +5483,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (pctOwned) { pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sdi.getUrl(), logProgress); } - ok = startInner(valContext, errors, resource, element, sdi, stack, false, pct, mode.withSource(ProfileSource.ProfileDependency)) && ok; + ok = startInner(valContext, errors, resource, element, sdi, stack, false, pct, mode.withSource(ProfileSource.ProfileDependency), fromContained) && ok; if (pctOwned) { pct.done(); } @@ -5510,7 +5510,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (pctOwned) { pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sd.getVersionedUrl(), logProgress); } - ok = startInner(valContext, errors, resource, element, sd, stack, false, pct, mode.withSource(ProfileSource.GlobalProfile)) && ok; + ok = startInner(valContext, errors, resource, element, sd, stack, false, pct, mode.withSource(ProfileSource.GlobalProfile), fromContained) && ok; if (pctOwned) { pct.done(); } @@ -5611,7 +5611,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } - public boolean startInner(ValidationContext valContext, List errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, boolean checkSpecials, PercentageTracker pct, ValidationMode mode) { + public boolean startInner(ValidationContext valContext, List errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, boolean checkSpecials, PercentageTracker pct, ValidationMode mode, boolean fromContained) { // the first piece of business is to see if we've validated this resource against this profile before. // if we have (*or if we still are*), then we'll just return our existing errors boolean ok = true; @@ -5641,13 +5641,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat ok = false; } if (checkSpecials) { - ok = checkSpecials(valContext, errors, element, stack, checkSpecials, pct, mode) && ok; + ok = checkSpecials(valContext, errors, element, stack, checkSpecials, pct, mode, fromContained) && ok; ok = validateResourceRules(errors, element, stack) && ok; } return ok; } - public boolean checkSpecials(ValidationContext valContext, List errors, Element element, NodeStack stack, boolean checkSpecials, PercentageTracker pct, ValidationMode mode) { + public boolean checkSpecials(ValidationContext valContext, List errors, Element element, NodeStack stack, boolean checkSpecials, PercentageTracker pct, ValidationMode mode, boolean contained) { boolean ok = true; long t = System.nanoTime(); @@ -5663,7 +5663,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } if (isHL7Core(element) && !isExample()) { - ok = checkPublisherConsistency(errors, element, stack) && ok; + ok = checkPublisherConsistency(valContext, errors, element, stack, contained) && ok; } } if (element.getType().equals(BUNDLE)) { @@ -5709,11 +5709,38 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } - private boolean checkPublisherConsistency(List errors, Element element, NodeStack stack) { + private boolean checkPublisherConsistency(ValidationContext valContext, List errors, Element element, NodeStack stack, boolean contained) { String pub = element.getNamedChildValue("publisher", false); Base wgT = element.getExtensionValue(ToolingExtensions.EXT_WORKGROUP); String wg = wgT == null ? null : wgT.primitiveValue(); + + if (contained && wg == null) { + boolean ok = true; + Element container = valContext.getRootResource(); + if (element.hasExtension(ToolingExtensions.EXT_WORKGROUP)) { + // container already specified the HL7 WG, so we don't need to test + // but we're still going to test pub if it exists + if (pub != null) { + wgT = container.getExtensionValue(ToolingExtensions.EXT_WORKGROUP); + wg = wgT == null ? null : wgT.primitiveValue(); + HL7WorkGroup wgd = HL7WorkGroups.find(wg); + if (wgd != null) { + String rpub = "HL7 International / "+wgd.getName(); + ok = rpub.equals(pub); + if (!ok && wgd.getName2() != null) { + ok = ("HL7 International / "+wgd.getName2()).equals(pub); + warningOrError(pub.contains("/"), errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), ok, I18nConstants.VALIDATION_HL7_PUBLISHER_MISMATCH2, wg, rpub, "HL7 International / "+wgd.getName2(), pub); + } else { + warningOrError(pub.contains("/"), errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), ok, I18nConstants.VALIDATION_HL7_PUBLISHER_MISMATCH, wg, rpub, pub); + } + } + } + return ok; + } + } + + List urls = new ArrayList<>(); for (Element c : element.getChildren("contact")) { for (Element t : c.getChildren("telecom")) { @@ -5722,6 +5749,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } + if (rule(errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), wg != null, I18nConstants.VALIDATION_HL7_WG_NEEDED, ToolingExtensions.EXT_WORKGROUP)) { HL7WorkGroup wgd = HL7WorkGroups.find(wg); if (rule(errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), wgd != null, I18nConstants.VALIDATION_HL7_WG_UNKNOWN, wg)) { @@ -5739,7 +5767,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat Utilities.startsWithInList( wgd.getLink(), urls), I18nConstants.VALIDATION_HL7_WG_URL, wg, wgd.getLink()); return true; } - } + } + return false; } @@ -5924,7 +5953,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } - checkSpecials(valContext, errors, element, stack, ok, pct, mode); + checkSpecials(valContext, errors, element, stack, ok, pct, mode, true); if (typeForResource.getProfile().size() == 1) { long t = System.nanoTime(); @@ -5933,7 +5962,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_EXPL, special == null ? "??" : special.toHuman(), resourceName, typeForResource.getProfile().get(0).asStringValue())) { trackUsage(profile, valContext, element); - ok = validateResource(hc, errors, resource, element, profile, idstatus, stack, pct, mode, false) && ok; + ok = validateResource(hc, errors, resource, element, profile, idstatus, stack, pct, mode, false, special == SpecialElement.CONTAINED) && ok; } else { ok = false; } @@ -5945,7 +5974,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat trackUsage(profile, valContext, element); if (rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_TYPE, special == null ? "??" : special.toHuman(), resourceName)) { - ok = validateResource(hc, errors, resource, element, profile, idstatus, stack, pct, mode, false) && ok; + ok = validateResource(hc, errors, resource, element, profile, idstatus, stack, pct, mode, false, special == SpecialElement.CONTAINED) && ok; } else { ok = false; } @@ -5966,7 +5995,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat trackUsage(profile, valContext, element); List perrors = new ArrayList<>(); errorsList.add(perrors); - if (validateResource(hc, perrors, resource, element, profile, idstatus, stack, pct, mode, false)) { + if (validateResource(hc, perrors, resource, element, profile, idstatus, stack, pct, mode, false, special == SpecialElement.CONTAINED)) { bm.append(u.asStringValue()); matched++; } @@ -6228,7 +6257,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } String stype = ei.getElement().fhirType(); - if (!stype.equals(type)) { + if (stype == null || !stype.equals(type)) { if (checkDefn.isChoice()) { if (extensionUrl != null && !isAbsolute(extensionUrl)) { ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), ei.getPath(), false, I18nConstants.EXTENSION_PROF_TYPE, profile.getVersionedUrl(), type, stype) && ok; @@ -6655,7 +6684,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat boolean ok = true; // 3. report any definitions that have a cardinality problem for (ElementDefinition ed : childDefinitions.getList()) { - if (ed.getRepresentation().isEmpty()) { // ignore xml attributes + if (!ed.hasRepresentation(PropertyRepresentation.XHTML) && !ed.hasRepresentation(PropertyRepresentation.XMLTEXT)) { // xhtml.value is XMLText in slices = null; if (ed.hasSlicing()) { @@ -7043,9 +7072,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return true; } boolean ok = true; - if (debug) { - System.out.println("inv "+inv.getKey()+" on "+path+" in "+resource.fhirType()+" {{ "+inv.getExpression()+" }}"+time()); - } // we don't allow dom-3 to execute - it takes too long (and is wrong). // instead, we enforce it in code if ("dom-3".equals(inv.getKey())) { @@ -7123,7 +7149,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat * The actual base entry point for internal use (re-entrant) */ private boolean validateResource(ValidationContext valContext, List errors, Element resource, - Element element, StructureDefinition defn, IdStatus idstatus, NodeStack stack, PercentageTracker pct, ValidationMode mode, boolean forReference) throws FHIRException { + Element element, StructureDefinition defn, IdStatus idstatus, NodeStack stack, PercentageTracker pct, ValidationMode mode, boolean forReference, boolean fromContained) throws FHIRException { boolean ok = true; // check here if we call validation policy here, and then change it to the new interface @@ -7175,7 +7201,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat // validate if (rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), checkResourceName(defn, resourceName, element.getFormat()), I18nConstants.VALIDATION_VAL_PROFILE_WRONGTYPE, defn.getType(), resourceName, defn.getVersionedUrl())) { - ok = start(valContext, errors, element, element, defn, stack, pct, mode) && ok; // root is both definition and type + ok = start(valContext, errors, element, element, defn, stack, pct, mode, fromContained) && ok; // root is both definition and type } else { ok = false; } @@ -7215,6 +7241,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat Set baseRefs, List containedList, int i, Element contained) { NodeStack n = stack.push(contained, i, null, null); boolean found = isReferencedFromBase(contained, baseRefs, containedList, new ArrayList<>()); + ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, n, found, I18nConstants.CONTAINED_ORPHAN_DOM3, contained.getIdBase()) && ok; return ok; } @@ -7232,8 +7259,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (c != contained && !ignoreList.contains(c)) { // ignore list is to prevent getting into an unterminated loop Set refs = (Set) c.getUserData(ValidationContext.INTERNAL_REFERENCES_NAME); List ignoreList2 = new ArrayList(); - ignoreList.addAll(ignoreList); - ignoreList.add(c); + ignoreList2.addAll(ignoreList); + ignoreList2.add(c); if (refs != null && refs.contains(id) && isReferencedFromBase(c, baseRefs, containedList, ignoreList2)) { return true; } @@ -7266,11 +7293,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } String s = b.toString(); - if (debug) { - System.out.println("OK = "+ok+" for "+path); - System.out.println("Errs = "+errors.toString()); - System.out.println("Ids = "+s); - } +// if (debug) { +// System.out.println("OK = "+ok+" for "+path); +// System.out.println("Errs = "+errors.toString()); +// System.out.println("Ids = "+s); +// } return s; } @@ -7692,4 +7719,5 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } return this; } + }