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....
}
}
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;
}
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;
}
Aggregations