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 cbdac7100..b0a74ac63 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 @@ -165,6 +165,10 @@ public class JsonParser extends ParserBase { } public Element parse(List errors, JsonObject object) throws FHIRException { + return parse(errors, object, null); + } + + public Element parse(List errors, JsonObject object, String statedPath) throws FHIRException { if (object == null) { System.out.println("What?"); } @@ -187,16 +191,16 @@ public class JsonParser extends ParserBase { return null; } } - path = name; + path = statedPath == null ? name : statedPath; } else { name = sd.getType(); - path = sd.getTypeTail(); + path = statedPath == null ? sd.getTypeTail() : statedPath; } baseElement = new Element(name, new Property(context, sd.getSnapshot().getElement().get(0), sd, this.getProfileUtilities(), this.getContextUtilities())).setFormat(FhirFormat.JSON); checkObject(errors, object, baseElement, path); baseElement.markLocation(line(object), col(object)); baseElement.setType(name); - baseElement.setPath(baseElement.fhirTypeRoot()); + baseElement.setPath(statedPath == null ? baseElement.fhirTypeRoot() : statedPath); parseChildren(errors, path, object, baseElement, true, null); baseElement.numberChildren(); return baseElement; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/SHCParser.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/SHCParser.java index 3d9aa170e..1376bdcb6 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/SHCParser.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/SHCParser.java @@ -167,7 +167,7 @@ public class SHCParser extends ParserBase { return res; } // ok. all checks passed, we can now validate the bundle - bnd.setElement(jsonParser.parse(bnd.getErrors(), cs.getJsonObject("fhirBundle"))); + bnd.setElement(jsonParser.parse(bnd.getErrors(), cs.getJsonObject("fhirBundle"), path)); bnd.setElementPath(path); } return res; 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 d4ecb2c94..4b67270d9 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 @@ -992,7 +992,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat setParents(element); long t = System.nanoTime(); - NodeStack stack = new NodeStack(context, path, element, validationLanguage); + NodeStack stack = new NodeStack(context, null, 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, false); } else { @@ -4695,18 +4695,22 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else { // work back through the parent list - if any of them are bundles, try to resolve // the resource in the bundle + + // 2024-04-05 - must work through the element parents not the stack parents, as the stack is not necessarily reflective of the full parent list + Element focus = stack.getElement(); String fullUrl = null; // we're going to try to work this out as we go up - while (stack != null && stack.getElement() != null) { - if (stack.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY && fullUrl == null && stack.getParent() != null && stack.getParent().getElement().getName().equals(ENTRY)) { - String type = stack.getParent().getParent().getElement().getChildValue(TYPE); - fullUrl = stack.getParent().getElement().getChildValue(FULL_URL); // we don't try to resolve contained references across this boundary + while (focus != null) { + // track the stack while we can + if (focus.getSpecial() == SpecialElement.BUNDLE_ENTRY && fullUrl == null && focus != null && focus.getParentForValidator().getName().equals(ENTRY)) { + String type = focus.getParentForValidator().getChildValue(TYPE); + fullUrl = focus.getParentForValidator().getChildValue(FULL_URL); // we don't try to resolve contained references across this boundary if (fullUrl == null) - bh.see(rule(errors, NO_RULE_DATE, IssueType.REQUIRED, stack.getParent().getElement().line(), stack.getParent().getElement().col(), stack.getParent().getLiteralPath(), + bh.see(rule(errors, NO_RULE_DATE, IssueType.REQUIRED, focus.getParentForValidator().line(), focus.getParentForValidator().col(), focus.getParentForValidator().getPath(), Utilities.existsInList(type, "batch-response", "transaction-response") || fullUrl != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOFULLURL)); } - if (BUNDLE.equals(stack.getElement().getType())) { - String type = stack.getElement().getChildValue(TYPE); - IndexedElement res = getFromBundle(stack.getElement(), ref, fullUrl, errors, path, type, "transaction".equals(type), bh); + if (BUNDLE.equals(focus.getType())) { + String type = focus.getChildValue(TYPE); + IndexedElement res = getFromBundle(focus, ref, fullUrl, errors, path, type, "transaction".equals(type), bh); if (res == null) { return null; } else { @@ -4714,15 +4718,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat rr.setResource(res.getMatch()); rr.setFocus(res.getMatch()); rr.setExternal(false); - rr.setStack(stack.push(res.getEntry(), res.getIndex(), res.getEntry().getProperty().getDefinition(), - res.getEntry().getProperty().getDefinition()).push(res.getMatch(), -1, - res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition())); + rr.setStack(new NodeStack(context, null, res.getMatch(), validationLanguage)); + rr.setVia(stack); +// +// !stack.push(res.getEntry(), res.getIndex(), res.getEntry().getProperty().getDefinition(), +// res.getEntry().getProperty().getDefinition()).push(res.getMatch(), -1, +// res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition())); rr.getStack().pathComment(rr.getResource().fhirType()+"/"+rr.getResource().getIdBase()); return rr; } } - if (stack.getElement().getSpecial() == SpecialElement.PARAMETER && stack.getParent() != null) { - NodeStack tgt = findInParams(stack.getParent().getParent(), ref); + if (focus.getSpecial() == SpecialElement.PARAMETER && focus.getParentForValidator() != null) { + NodeStack tgt = findInParams(focus.getParentForValidator().getParentForValidator(), ref, stack); if (tgt != null) { ResolvedReference rr = new ResolvedReference(); rr.setResource(tgt.getElement()); @@ -4733,7 +4740,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return rr; } } - stack = stack.getParent(); + focus = focus.getParentForValidator(); } // we can get here if we got called via FHIRPath conformsTo which breaks the stack continuity. if (groupingResource != null && BUNDLE.equals(groupingResource.fhirType())) { // it could also be a Parameters resource - that case isn't handled yet @@ -4759,10 +4766,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return null; } - private NodeStack findInParams(NodeStack params, String ref) { + private NodeStack findInParams(Element params, String ref, NodeStack stack) { int i = 0; - for (Element child : params.getElement().getChildren("parameter")) { - NodeStack p = params.push(child, i, child.getProperty().getDefinition(), child.getProperty().getDefinition()); + for (Element child : params.getChildren("parameter")) { + NodeStack p = stack.push(child, i, child.getProperty().getDefinition(), child.getProperty().getDefinition()); if (child.hasChild("resource", false)) { Element res = child.getNamedChild("resource", false); if ((res.fhirType()+"/"+res.getIdBase()).equals(ref)) { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/NodeStack.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/NodeStack.java index 4f3a0ba4b..687e8e90f 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/NodeStack.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/NodeStack.java @@ -37,7 +37,7 @@ public class NodeStack { this.context = context; ids = new HashMap<>(); this.element = element; - literalPath = (initialPath == null ? "" : initialPath+".") + element.getPath(); + literalPath = (initialPath == null ? "" : initialPath+".") + buildPathForElement(element, true); workingLang = validationLanguage; if (!element.getName().equals(element.fhirType())) { logicalPaths = new HashSet<>(); @@ -45,6 +45,21 @@ public class NodeStack { } } + private String buildPathForElement(Element e, boolean first) { + if (e.getParentForValidator() != null) { + String node = e.getName().contains("/") ? e.getName().substring(e.getName().lastIndexOf("/")+1) : e.getName(); + if (e.hasIndex() && e.getProperty().isList() && e.getSpecial() == null) { + node = node+"["+Integer.toString(e.getIndex())+"]"; + } + if (!first && e.isResource()) { + node = node +"/*"+e.fhirType()+"/"+e.getIdBase()+"*/"; + } + return buildPathForElement(e.getParentForValidator(), false)+"."+node; + } else { + return e.getPath(); + } + } + public NodeStack(IWorkerContext context, Element element, String refPath, String validationLanguage) { this.context = context; ids = new HashMap<>(); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ResolvedReference.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ResolvedReference.java index 18c3a010a..354b1a4b9 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ResolvedReference.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ResolvedReference.java @@ -9,6 +9,7 @@ public class ResolvedReference { private Element focus; private boolean external; private NodeStack stack; + private NodeStack via; public ResolvedReference setResource(Element resource) { this.resource = resource; @@ -50,6 +51,14 @@ public class ResolvedReference { return focus; } + public NodeStack getVia() { + return via; + } + + public void setVia(NodeStack via) { + this.via = via; + } + public ValidationContext valContext(ValidationContext valContext, StructureDefinition profile) { if (external) { return valContext.forRemoteReference(profile, resource);