Search in sources :

Example 1 with ResolvedReference

use of org.hl7.fhir.validation.instance.utils.ResolvedReference in project org.hl7.fhir.core by hapifhir.

the class InstanceValidator method checkReference.

private void checkReference(ValidatorHostContext hostContext, List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, ElementDefinition container, String parentType, NodeStack stack) throws FHIRException {
    Reference reference = ObjectConverter.readAsReference(element);
    String ref = reference.getReference();
    if (Utilities.noString(ref)) {
        if (!path.contains("element.pattern")) {
            // this business rule doesn't apply to patterns
            if (Utilities.noString(reference.getIdentifier().getSystem()) && Utilities.noString(reference.getIdentifier().getValue())) {
                warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, !Utilities.noString(element.getNamedChildValue("display")), I18nConstants.REFERENCE_REF_NODISPLAY);
            }
        }
        return;
    } else if (Utilities.existsInList(ref, "http://tools.ietf.org/html/bcp47")) {
        // special known URLs that can't be validated but are known to be valid
        return;
    }
    warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, !isSuspiciousReference(ref), I18nConstants.REFERENCE_REF_SUSPICIOUS, ref);
    ResolvedReference we = localResolve(ref, stack, errors, path, hostContext.getRootResource(), hostContext.getGroupingResource(), element);
    String refType;
    if (ref.startsWith("#")) {
        refType = "contained";
    } else {
        if (we == null) {
            refType = "remote";
        } else {
            refType = "bundled";
        }
    }
    ReferenceValidationPolicy pol;
    if (refType.equals("contained") || refType.equals("bundled")) {
        pol = ReferenceValidationPolicy.CHECK_VALID;
    } else {
        if (policyAdvisor == null)
            pol = ReferenceValidationPolicy.IGNORE;
        else
            pol = policyAdvisor.policyForReference(this, hostContext.getAppContext(), path, ref);
    }
    if (pol.checkExists()) {
        if (we == null) {
            if (!refType.equals("contained")) {
                if (fetcher == null) {
                    throw new FHIRException(context.formatMessage(I18nConstants.RESOURCE_RESOLUTION_SERVICES_NOT_PROVIDED));
                } else {
                    Element ext = null;
                    if (fetchCache.containsKey(ref)) {
                        ext = fetchCache.get(ref);
                    } else {
                        try {
                            ext = fetcher.fetch(this, hostContext.getAppContext(), ref);
                        } catch (IOException e) {
                            if (STACK_TRACE)
                                e.printStackTrace();
                            throw new FHIRException(e);
                        }
                        if (ext != null) {
                            setParents(ext);
                            fetchCache.put(ref, ext);
                        }
                    }
                    we = ext == null ? null : makeExternalRef(ext, path);
                }
            }
        }
        boolean ok = (allowExamples && (ref.contains("example.org") || ref.contains("acme.com"))) || (we != null || pol == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS);
        rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok, I18nConstants.REFERENCE_REF_CANTRESOLVE, ref);
    }
    String ft;
    if (we != null) {
        ft = we.getType();
    } else {
        ft = tryParse(ref);
    }
    if (reference.hasType()) {
        // R4 onwards...
        // the type has to match the specified
        String tu = isAbsolute(reference.getType()) ? reference.getType() : "http://hl7.org/fhir/StructureDefinition/" + reference.getType();
        TypeRefComponent containerType = container.getType("Reference");
        if (!containerType.hasTargetProfile(tu) && !containerType.hasTargetProfile("http://hl7.org/fhir/StructureDefinition/Resource") && !containerType.getTargetProfile().isEmpty()) {
            boolean matchingResource = false;
            for (CanonicalType target : containerType.getTargetProfile()) {
                StructureDefinition sd = resolveProfile(profile, target.asStringValue());
                if (rule(errors, IssueType.NOTFOUND, element.line(), element.col(), path, sd != null, I18nConstants.REFERENCE_REF_CANTRESOLVEPROFILE, target.asStringValue())) {
                    if (("http://hl7.org/fhir/StructureDefinition/" + sd.getType()).equals(tu)) {
                        matchingResource = true;
                        break;
                    }
                }
            }
            rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, matchingResource, I18nConstants.REFERENCE_REF_WRONGTARGET, reference.getType(), container.getType("Reference").getTargetProfile());
        }
        // the type has to match the actual
        rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft == null || ft.equals(reference.getType()), I18nConstants.REFERENCE_REF_BADTARGETTYPE, reference.getType(), ft);
    }
    if (we != null && pol.checkType()) {
        if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft != null, I18nConstants.REFERENCE_REF_NOTYPE)) {
            // we validate as much as we can. First, can we infer a type from the profile?
            boolean ok = false;
            TypeRefComponent type = getReferenceTypeRef(container.getType());
            if (type.hasTargetProfile() && !type.hasTargetProfile("http://hl7.org/fhir/StructureDefinition/Resource")) {
                Set<String> types = new HashSet<>();
                List<StructureDefinition> profiles = new ArrayList<>();
                for (UriType u : type.getTargetProfile()) {
                    StructureDefinition sd = resolveProfile(profile, u.getValue());
                    if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, sd != null, I18nConstants.REFERENCE_REF_CANTRESOLVEPROFILE, u.getValue())) {
                        types.add(sd.getType());
                        if (ft.equals(sd.getType())) {
                            ok = true;
                            profiles.add(sd);
                        }
                    }
                }
                if (!pol.checkValid()) {
                    rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() > 0, I18nConstants.REFERENCE_REF_CANTMATCHTYPE, ref, StringUtils.join("; ", type.getTargetProfile()));
                } else {
                    Map<StructureDefinition, List<ValidationMessage>> badProfiles = new HashMap<>();
                    Map<StructureDefinition, List<ValidationMessage>> goodProfiles = new HashMap<>();
                    int goodCount = 0;
                    for (StructureDefinition pr : profiles) {
                        List<ValidationMessage> profileErrors = new ArrayList<ValidationMessage>();
                        validateResource(we.hostContext(hostContext, pr), profileErrors, we.getResource(), we.getFocus(), pr, IdStatus.OPTIONAL, we.getStack().resetIds());
                        if (!hasErrors(profileErrors)) {
                            goodCount++;
                            goodProfiles.put(pr, profileErrors);
                            trackUsage(pr, hostContext, element);
                        } else {
                            badProfiles.put(pr, profileErrors);
                        }
                    }
                    if (goodCount == 1) {
                        if (showMessagesFromReferences) {
                            for (ValidationMessage vm : goodProfiles.values().iterator().next()) {
                                if (!errors.contains(vm)) {
                                    errors.add(vm);
                                }
                            }
                        }
                    } else if (goodProfiles.size() == 0) {
                        if (!isShowMessagesFromReferences()) {
                            rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, areAllBaseProfiles(profiles), I18nConstants.REFERENCE_REF_CANTMATCHCHOICE, ref, asList(type.getTargetProfile()));
                            for (StructureDefinition sd : badProfiles.keySet()) {
                                slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, false, context.formatMessage(I18nConstants.DETAILS_FOR__MATCHING_AGAINST_PROFILE_, ref, sd.getUrl()), errorSummaryForSlicingAsHtml(badProfiles.get(sd)), errorSummaryForSlicingAsText(badProfiles.get(sd)));
                            }
                        } else {
                            rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() == 1, I18nConstants.REFERENCE_REF_CANTMATCHCHOICE, ref, asList(type.getTargetProfile()));
                            for (List<ValidationMessage> messages : badProfiles.values()) {
                                for (ValidationMessage vm : messages) {
                                    if (!errors.contains(vm)) {
                                        errors.add(vm);
                                    }
                                }
                            }
                        }
                    } else {
                        if (!isShowMessagesFromReferences()) {
                            warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, I18nConstants.REFERENCE_REF_MULTIPLEMATCHES, ref, asListByUrl(goodProfiles.keySet()));
                            for (StructureDefinition sd : badProfiles.keySet()) {
                                slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, false, context.formatMessage(I18nConstants.DETAILS_FOR__MATCHING_AGAINST_PROFILE_, ref, sd.getUrl()), errorSummaryForSlicingAsHtml(badProfiles.get(sd)), errorSummaryForSlicingAsText(badProfiles.get(sd)));
                            }
                        } else {
                            warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, I18nConstants.REFERENCE_REF_MULTIPLEMATCHES, ref, asListByUrl(goodProfiles.keySet()));
                            for (List<ValidationMessage> messages : goodProfiles.values()) {
                                for (ValidationMessage vm : messages) {
                                    if (!errors.contains(vm)) {
                                        errors.add(vm);
                                    }
                                }
                            }
                        }
                    }
                }
                rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok, I18nConstants.REFERENCE_REF_BADTARGETTYPE, ft, types.toString());
            }
            if (type.hasAggregation() && !noCheckAggregation) {
                boolean modeOk = false;
                CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
                for (Enumeration<AggregationMode> mode : type.getAggregation()) {
                    b.append(mode.getCode());
                    if (mode.getValue().equals(AggregationMode.CONTAINED) && refType.equals("contained"))
                        modeOk = true;
                    else if (mode.getValue().equals(AggregationMode.BUNDLED) && refType.equals("bundled"))
                        modeOk = true;
                    else if (mode.getValue().equals(AggregationMode.REFERENCED) && (refType.equals("bundled") || refType.equals("remote")))
                        modeOk = true;
                }
                rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, modeOk, I18nConstants.REFERENCE_REF_AGGREGATION, refType, b.toString());
            }
        }
    }
    if (we == null) {
        TypeRefComponent type = getReferenceTypeRef(container.getType());
        boolean okToRef = !type.hasAggregation() || type.hasAggregation(AggregationMode.REFERENCED);
        rule(errors, IssueType.REQUIRED, -1, -1, path, okToRef, I18nConstants.REFERENCE_REF_NOTFOUND_BUNDLE, ref);
    }
    if (we == null && ft != null && assumeValidRestReferences) {
        // if we == null, we inferred ft from the reference. if we are told to treat this as gospel
        TypeRefComponent type = getReferenceTypeRef(container.getType());
        Set<String> types = new HashSet<>();
        StructureDefinition sdFT = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + ft);
        boolean ok = false;
        for (CanonicalType tp : type.getTargetProfile()) {
            StructureDefinition sd = context.fetchResource(StructureDefinition.class, tp.getValue());
            if (sd != null) {
                types.add(sd.getType());
            }
            StructureDefinition sdF = sdFT;
            while (sdF != null) {
                if (sdF.getType().equals(sd.getType())) {
                    ok = true;
                    break;
                }
                sdF = sdF.hasBaseDefinition() ? context.fetchResource(StructureDefinition.class, sdF.getBaseDefinition()) : null;
            }
        }
        rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, types.isEmpty() || ok, I18nConstants.REFERENCE_REF_BADTARGETTYPE2, ft, ref, types);
    }
    if (pol == ReferenceValidationPolicy.CHECK_VALID) {
    // todo....
    }
}
Also used : ResolvedReference(org.hl7.fhir.validation.instance.utils.ResolvedReference) ValidationMessage(org.hl7.fhir.utilities.validation.ValidationMessage) HashMap(java.util.HashMap) NamedElement(org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement) IndexedElement(org.hl7.fhir.validation.instance.utils.IndexedElement) SpecialElement(org.hl7.fhir.r5.elementmodel.Element.SpecialElement) Element(org.hl7.fhir.r5.elementmodel.Element) ArrayList(java.util.ArrayList) AggregationMode(org.hl7.fhir.r5.model.ElementDefinition.AggregationMode) CanonicalType(org.hl7.fhir.r5.model.CanonicalType) UriType(org.hl7.fhir.r5.model.UriType) StructureDefinition(org.hl7.fhir.r5.model.StructureDefinition) TypeRefComponent(org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent) ArrayList(java.util.ArrayList) List(java.util.List) HashSet(java.util.HashSet) Reference(org.hl7.fhir.r5.model.Reference) ResolvedReference(org.hl7.fhir.validation.instance.utils.ResolvedReference) CommaSeparatedStringBuilder(org.hl7.fhir.utilities.CommaSeparatedStringBuilder) IOException(java.io.IOException) FHIRException(org.hl7.fhir.exceptions.FHIRException) ContactPoint(org.hl7.fhir.r5.model.ContactPoint)

Example 2 with ResolvedReference

use of org.hl7.fhir.validation.instance.utils.ResolvedReference in project org.hl7.fhir.core by hapifhir.

the class InstanceValidator method makeExternalRef.

private ResolvedReference makeExternalRef(Element external, String path) {
    ResolvedReference res = new ResolvedReference();
    res.setResource(external);
    res.setFocus(external);
    res.setExternal(true);
    res.setStack(new NodeStack(context, external, path, validationLanguage));
    return res;
}
Also used : ResolvedReference(org.hl7.fhir.validation.instance.utils.ResolvedReference) NodeStack(org.hl7.fhir.validation.instance.utils.NodeStack)

Example 3 with ResolvedReference

use of org.hl7.fhir.validation.instance.utils.ResolvedReference in project org.hl7.fhir.core by hapifhir.

the class InstanceValidator method localResolve.

private ResolvedReference localResolve(String ref, NodeStack stack, List<ValidationMessage> errors, String path, Element rootResource, Element groupingResource, Element source) {
    if (ref.startsWith("#")) {
        // work back through the parent list.
        // 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;
        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("#") && nstack.getElement().getSpecial() != SpecialElement.CONTAINED && wasContained) {
                    ResolvedReference rr = new ResolvedReference();
                    rr.setResource(nstack.getElement());
                    rr.setFocus(nstack.getElement());
                    rr.setExternal(false);
                    rr.setStack(nstack);
                    // System.out.println("-->"+nstack.getLiteralPath());
                    return rr;
                }
                if (nstack.getElement().getSpecial() == SpecialElement.CONTAINED) {
                    wasContained = true;
                }
                IndexedElement res = getContainedById(nstack.getElement(), ref.substring(1));
                if (res != null) {
                    ResolvedReference rr = new ResolvedReference();
                    rr.setResource(nstack.getElement());
                    rr.setFocus(res.getMatch());
                    rr.setExternal(false);
                    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 (nstack.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY || nstack.getElement().getSpecial() == SpecialElement.PARAMETER) {
                // we don't try to resolve contained references across this boundary
                return null;
            }
            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 {
        // work back through the parent list - if any of them are bundles, try to resolve
        // the resource in the bundle
        // we're going to try to work this out as we go up
        String fullUrl = null;
        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);
                // we don't try to resolve contained references across this boundary
                fullUrl = stack.getParent().getElement().getChildValue(FULL_URL);
                if (fullUrl == null)
                    rule(errors, IssueType.REQUIRED, stack.getParent().getElement().line(), stack.getParent().getElement().col(), stack.getParent().getLiteralPath(), 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));
                if (res == null) {
                    return null;
                } else {
                    ResolvedReference rr = new ResolvedReference();
                    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.getStack().qualifyPath(".ofType(" + rr.getResource().fhirType() + ")");
                    return rr;
                }
            }
            if (stack.getElement().getSpecial() == SpecialElement.PARAMETER && stack.getParent() != null) {
                NodeStack tgt = findInParams(stack.getParent().getParent(), ref);
                if (tgt != null) {
                    ResolvedReference rr = new ResolvedReference();
                    rr.setResource(tgt.getElement());
                    rr.setFocus(tgt.getElement());
                    rr.setExternal(false);
                    rr.setStack(tgt);
                    rr.getStack().qualifyPath(".ofType(" + tgt.getElement().fhirType() + ")");
                    return rr;
                }
            }
            stack = stack.getParent();
        }
        // 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
            String type = groupingResource.getChildValue(TYPE);
            Element entry = getEntryForSource(groupingResource, source);
            fullUrl = entry.getChildValue(FULL_URL);
            IndexedElement res = getFromBundle(groupingResource, ref, fullUrl, errors, path, type, "transaction".equals(type));
            if (res == null) {
                return null;
            } else {
                ResolvedReference rr = new ResolvedReference();
                rr.setResource(res.getMatch());
                rr.setFocus(res.getMatch());
                rr.setExternal(false);
                rr.setStack(new NodeStack(context, null, rootResource, validationLanguage).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().qualifyPath(".ofType(" + rr.getResource().fhirType() + ")");
                return rr;
            }
        }
    }
    return null;
}
Also used : ResolvedReference(org.hl7.fhir.validation.instance.utils.ResolvedReference) IndexedElement(org.hl7.fhir.validation.instance.utils.IndexedElement) NamedElement(org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement) IndexedElement(org.hl7.fhir.validation.instance.utils.IndexedElement) SpecialElement(org.hl7.fhir.r5.elementmodel.Element.SpecialElement) Element(org.hl7.fhir.r5.elementmodel.Element) NodeStack(org.hl7.fhir.validation.instance.utils.NodeStack)

Aggregations

ResolvedReference (org.hl7.fhir.validation.instance.utils.ResolvedReference)3 Element (org.hl7.fhir.r5.elementmodel.Element)2 SpecialElement (org.hl7.fhir.r5.elementmodel.Element.SpecialElement)2 NamedElement (org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement)2 IndexedElement (org.hl7.fhir.validation.instance.utils.IndexedElement)2 NodeStack (org.hl7.fhir.validation.instance.utils.NodeStack)2 IOException (java.io.IOException)1 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1 List (java.util.List)1 FHIRException (org.hl7.fhir.exceptions.FHIRException)1 CanonicalType (org.hl7.fhir.r5.model.CanonicalType)1 ContactPoint (org.hl7.fhir.r5.model.ContactPoint)1 AggregationMode (org.hl7.fhir.r5.model.ElementDefinition.AggregationMode)1 TypeRefComponent (org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent)1 Reference (org.hl7.fhir.r5.model.Reference)1 StructureDefinition (org.hl7.fhir.r5.model.StructureDefinition)1 UriType (org.hl7.fhir.r5.model.UriType)1 CommaSeparatedStringBuilder (org.hl7.fhir.utilities.CommaSeparatedStringBuilder)1