diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/SpecMapUnpacker.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/SpecMapUnpacker.java new file mode 100644 index 000000000..b5640a4e4 --- /dev/null +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/SpecMapUnpacker.java @@ -0,0 +1,56 @@ +package org.hl7.fhir.convertors.misc; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import org.hl7.fhir.utilities.TextFile; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.filesystem.DirectoryVisitor; +import org.hl7.fhir.utilities.filesystem.DirectoryVisitor.IDirectoryVisitorImplementation; +import org.hl7.fhir.utilities.filesystem.ManagedFileAccess; +import org.hl7.fhir.utilities.npm.NpmPackage; + +public class SpecMapUnpacker { + + public static void main(String[] args) throws IOException { + new SpecMapUnpacker().unpack(args[0]); + } + + public class SpecMapScanner implements IDirectoryVisitorImplementation { + + @Override + public boolean enterDirectory(File directory) throws IOException { + return true; + } + + @Override + public boolean visitFile(File file) throws IOException { + System.out.println("Look at "+file.getAbsolutePath()); + try { + NpmPackage npm = NpmPackage.fromPackage(ManagedFileAccess.inStream(file)); + if (npm.hasFile("other", "spec.internals")) { + byte[] cnt = TextFile.streamToBytes(npm.load("other", "spec.internals")); + TextFile.bytesToFile(cnt, Utilities.path(Utilities.getDirectoryForFile(file.getAbsolutePath()), "page-map.json")); + System.out.println(" ...extracted"); + return true; + } else { + System.out.println(" ...not found"); + return false; + } + + } catch (Exception e) { + System.out.println(" ...error: "+e.getMessage()); + return false; + } + } + + } + + private void unpack(String path) throws IOException { + System.out.println("Scanning "+path); + int count = DirectoryVisitor.visitDirectory(new SpecMapScanner(), path, "tgz"); + System.out.println("Done. "+count+" files extracted"); + } + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java index 407dcfcb1..b5382e9de 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java @@ -700,9 +700,11 @@ public class ProfileUtilities { throw new DefinitionException(context.formatMessage(I18nConstants.UNABLE_TO_FIND_BASE__FOR_, base.getBaseDefinition(), base.getUrl())); checkNotGenerating(sdb, "an extension base"); generateSnapshot(sdb, base, base.getUrl(), (sdb.hasWebPath()) ? Utilities.extractBaseUrl(sdb.getWebPath()) : webUrl, base.getName()); - } fixTypeOfResourceId(base); + if (base.hasExtension(ToolingExtensions.EXT_TYPE_PARAMETER)) { + checkTypeParameters(base, derived); + } if (snapshotStack.contains(derived.getUrl())) { throw new DefinitionException(context.formatMessage(I18nConstants.CIRCULAR_SNAPSHOT_REFERENCES_DETECTED_CANNOT_GENERATE_SNAPSHOT_STACK__, snapshotStack.toString())); @@ -976,6 +978,29 @@ public class ProfileUtilities { } + private void checkTypeParameters(StructureDefinition base, StructureDefinition derived) { + String bt = ToolingExtensions.readStringExtension(base, ToolingExtensions.EXT_TYPE_PARAMETER); + if (!derived.hasExtension(ToolingExtensions.EXT_TYPE_PARAMETER)) { + throw new DefinitionException(context.formatMessage(I18nConstants.SD_TYPE_PARAMETER_MISSING, base.getVersionedUrl(), bt, derived.getVersionedUrl())); + } + String dt = ToolingExtensions.readStringExtension(derived, ToolingExtensions.EXT_TYPE_PARAMETER); + StructureDefinition bsd = context.fetchTypeDefinition(bt); + StructureDefinition dsd = context.fetchTypeDefinition(dt); + if (bsd == null) { + throw new DefinitionException(context.formatMessage(I18nConstants.SD_TYPE_PARAMETER_UNKNOWN, base.getVersionedUrl(), bt)); + } + if (dsd == null) { + throw new DefinitionException(context.formatMessage(I18nConstants.SD_TYPE_PARAMETER_UNKNOWN, derived.getVersionedUrl(), dt)); + } + StructureDefinition t = dsd; + while (t != bsd && t != null) { + t = context.fetchResource(StructureDefinition.class, t.getBaseDefinition()); + } + if (t == null) { + throw new DefinitionException(context.formatMessage(I18nConstants.SD_TYPE_PARAMETER_INVALID, base.getVersionedUrl(), bt, derived.getVersionedUrl(), dt)); + } + } + private XVerExtensionManager makeXVer() { if (xver == null) { xver = new XVerExtensionManager(context); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java index e65cfde9e..55e42979c 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java @@ -1483,7 +1483,7 @@ public class DataRenderer extends Renderer implements CodeResolver { x.addText(displayContactPoint(contact)); break; case PHONE: - if (contact.hasValue() && contact.getValue().startsWith("+")) { + if (contact.hasValue() && contact.getValue() != null && contact.getValue().startsWith("+")) { x.ah("tel:"+contact.getValue().replace(" ", "")).tx(contact.getValue()); } else { x.addText(displayContactPoint(contact)); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ExampleScenarioRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ExampleScenarioRenderer.java index 43ed0df1b..ed3439d5a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ExampleScenarioRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ExampleScenarioRenderer.java @@ -105,7 +105,7 @@ public class ExampleScenarioRenderer extends TerminologyRenderer { for (ExampleScenarioProcessStepComponent step: process.getStep()) { plantUml += toPlantUml(step, stepPrefix(prefix, step, stepCount), scen, actorsActive, actorKeys); if (step.getPause()) - plantUml += context.formatPhrase(RenderingContext.EX_SCEN_TIME); + plantUml += context.formatPhrase(RenderingContext.EX_SCEN_TIME)+"\n"; stepCount++; } 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 69dc03af7..f35b0c8f0 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 @@ -494,7 +494,7 @@ public abstract class ResourceRenderer extends DataRenderer { if (url.startsWith("#") && res != null) { for (ResourceWrapper r : res.getContained()) { if (r.getId().equals(url.substring(1))) - return new ResourceWithReference(ResourceReferenceKind.CONTAINED, null, r); + return new ResourceWithReference(ResourceReferenceKind.CONTAINED, url, r); } return null; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java index 7587cb370..976ae6faf 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java @@ -79,7 +79,7 @@ import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; import org.hl7.fhir.r5.terminologies.utilities.ValidationResult; import org.hl7.fhir.r5.utils.PublicationHacker; import org.hl7.fhir.r5.utils.ToolingExtensions; -import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; +import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.MarkDownProcessor; import org.hl7.fhir.utilities.StandardsStatus; import org.hl7.fhir.utilities.TextFile; @@ -1060,6 +1060,19 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } Cell left = gen.new Cell(null, ref, sName, hint, null); row.getCells().add(left); + if (profile.hasExtension(ToolingExtensions.EXT_TYPE_PARAMETER)) { + Extension etp = profile.getExtensionByUrl(ToolingExtensions.EXT_TYPE_PARAMETER); + String name = etp.getExtensionString("name"); + String type = etp.getExtensionString("type"); + StructureDefinition t = context.getContext().fetchTypeDefinition(type); + if (t == null) { + left.addPiece(gen.new Piece(null, "<"+name+" : "+type+">", null)); + } else if (t.getWebPath() == null) { + left.addPiece(gen.new Piece(type, "<"+name+" : "+t.present()+">", null)); + } else { + left.addPiece(gen.new Piece(t.getWebPath(), "<"+name+" : "+t.present()+">", null)); + } + } return left; } @@ -1964,6 +1977,28 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } else { c.addPiece(checkForNoChange(t, gen.new Piece(null, tc, null))); } + if (t.hasExtension(ToolingExtensions.EXT_TYPE_PARAMETER)) { + c.addPiece(checkForNoChange(t, gen.new Piece(null, "<", null))); + boolean pfirst = true; + List exl = t.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_PARAMETER); + for (Extension ex : exl) { + if (pfirst) { pfirst = false; } else { c.addPiece(checkForNoChange(t, gen.new Piece(null, ";", null))); } + if (exl.size() > 1) { + c.addPiece(checkForNoChange(t, gen.new Piece(null, ex.getExtensionString("name")+": ", null))); + } + String type = ex.getExtensionString("type"); + StructureDefinition psd = context.getContext().fetchTypeDefinition(type); + if (psd == null) { + c.addPiece(checkForNoChange(t, gen.new Piece(null, type, null))); + } else if (psd.getWebPath() == null) { + c.addPiece(checkForNoChange(t, gen.new Piece(type, psd.present(), null))); + } else { + c.addPiece(checkForNoChange(t, gen.new Piece(psd.getWebPath(), psd.present(), null))); + } + } + c.addPiece(checkForNoChange(t, gen.new Piece(null, ">", null))); + + } if (!mustSupportMode && isMustSupportDirect(t) && e.getMustSupport()) { c.addPiece(gen.new Piece(null, " ", null)); c.addStyledText((context.formatPhrase(RenderingContext.STRUC_DEF_TYPE_SUPP)), "S", "white", "red", null, false); @@ -3670,6 +3705,9 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } else { tableRow(tbl, context.formatPhrase(RenderingContext.GENERAL_TYPE), "datatypes.html", strikethrough, describeTypes(d.getType(), false, d, compare, mode, value, compareValue, sd)); } + if (root && sd.hasExtension(ToolingExtensions.EXT_TYPE_PARAMETER)) { + tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_TYPE_PARAMETER), "http://hl7.org/fhir/tools/StructureDefinition-type-parameter.html", strikethrough, renderTypeParameter(sd.getExtensionByUrl(ToolingExtensions.EXT_TYPE_PARAMETER))); + } if (d.hasExtension(ToolingExtensions.EXT_DEF_TYPE)) { tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_DEFAULT_TYPE), "datatypes.html", strikethrough, ToolingExtensions.readStringExtension(d, ToolingExtensions.EXT_DEF_TYPE)); } @@ -3830,6 +3868,20 @@ public class StructureDefinitionRenderer extends ResourceRenderer { tbl.tx("\r\n"); } + private XhtmlNode renderTypeParameter(Extension ext) { + XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); + x.tx(ext.getExtensionString("name")); + x.tx(" : "); + String t = ext.getExtensionString("type"); + StructureDefinition sd = context.getContext().fetchTypeDefinition(t); + if (sd == null) { + x.code().tx(t); + } else { + x.ah(sd.getWebPath(), t).tx(sd.present()); + } + return x; + } + private XhtmlNode presentModifier(ElementDefinition d, int mode, ElementDefinition compare) throws FHIRException, IOException { XhtmlNode x1 = compareString(encodeValue(d.getIsModifierElement(), null), d.getIsModifierElement(), null, "isModifier", d, compare == null ? null : encodeValue(compare.getIsModifierElement(), null), null, mode, false, false); if (x1 != null) { @@ -4318,7 +4370,28 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } else { ts = compareString(x, t.getWorkingCode(), t, getTypeLink(t, sd), "code", t, compare==null ? null : compare.getWorkingCode(), compare==null ? null : getTypeLink(compare, sd), mode, false, false); } - + if (t.hasExtension(ToolingExtensions.EXT_TYPE_PARAMETER)) { + x.tx("<"); + boolean first = true; + List exl = t.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_PARAMETER); + for (Extension ex : exl) { + if (first) { first = false; } else { x.tx("; "); } + if (exl.size() > 1) { + x.tx(ex.getExtensionString("name")); + x.tx(":"); + } + String type = ex.getExtensionString("type"); + StructureDefinition psd = context.getContext().fetchTypeDefinition(type); + if (psd == null) { + x.code().tx(type); + } else if (psd.getWebPath() == null) { + x.ah(type).tx(type); + } else { + x.ah(psd.getWebPath()).tx(type); + } + } + x.tx(">"); + } if ((!mustSupportOnly && (t.hasProfile() || (compare!=null && compare.hasProfile()))) || isMustSupport(t.getProfile())) { StatusList profiles = analyseProfiles(t.getProfile(), compare == null ? null : compare.getProfile(), mustSupportOnly, mode); if (profiles.size() > 0) { @@ -4647,6 +4720,9 @@ public class StructureDefinitionRenderer extends ResourceRenderer { break; } } + if (Utilities.noString(newMap) && compare == null) { + return null; + } if (compare==null) return new XhtmlNode(NodeType.Element, "div").tx(newMap); String oldMap = null; @@ -4656,7 +4732,9 @@ public class StructureDefinitionRenderer extends ResourceRenderer { break; } } - + if (Utilities.noString(newMap) && Utilities.noString(oldMap)) { + return null; + } return compareString(Utilities.escapeXml(newMap), null, null, "mapping", d, Utilities.escapeXml(oldMap), null, mode, false, false); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java index 3620e3056..a16b2da46 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java @@ -162,7 +162,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { try { T output = (T) client.issueGetResourceRequest(resourceUri, withVer(preferredResourceFormat.getHeader(), "5.0"), - generateHeaders(), + generateHeaders(false), message, timeoutNormal).getReference(); if (attemptedResourceFormat != preferredResourceFormat) { @@ -214,7 +214,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { ResourceRequest result = null; try { result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), - withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(), "Read " + resourceClass + "/" + id, + withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "Read " + resourceClass + "/" + id, timeoutNormal); if (result.isUnsuccessfulRequest()) { throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), @@ -233,7 +233,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { try { result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), withVer(getPreferredResourceFormat(), "4.0"), - generateHeaders(), + generateHeaders(false), "Read " + resourceClass.getName() + "/" + id, timeoutNormal); if (result.isUnsuccessfulRequest()) { @@ -251,7 +251,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { try { result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndIdAndVersion(resourceClass, id, version), withVer(getPreferredResourceFormat(), "4.0"), - generateHeaders(), + generateHeaders(false), "VRead " + resourceClass.getName() + "/" + id + "/?_history/" + version, timeoutNormal); if (result.isUnsuccessfulRequest()) { @@ -269,7 +269,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { try { result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndCanonical(resourceClass, canonicalURL), withVer(getPreferredResourceFormat(), "4.0"), - generateHeaders(), + generateHeaders(false), "Read " + resourceClass.getName() + "?url=" + canonicalURL, timeoutNormal); if (result.isUnsuccessfulRequest()) { @@ -293,7 +293,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resource.getClass(), resource.getId()), ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false), withVer(getPreferredResourceFormat(), "4.0"), - generateHeaders(), + generateHeaders(true), "Update " + resource.fhirType() + "/" + resource.getId(), timeoutOperation); if (result.isUnsuccessfulRequest()) { @@ -321,7 +321,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false), withVer(getPreferredResourceFormat(), "4.0"), - generateHeaders(), + generateHeaders(true), "Update " + resource.fhirType() + "/" + id, timeoutOperation); if (result.isUnsuccessfulRequest()) { @@ -357,10 +357,10 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { URI url = resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps); if (complex) { byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true); - result = client.issuePostRequest(url, body, withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(), + result = client.issuePostRequest(url, body, withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), "POST " + resourceClass.getName() + "/$" + name, timeoutLong); } else { - result = client.issueGetResourceRequest(url, withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(), "GET " + resourceClass.getName() + "/$" + name, timeoutLong); + result = client.issueGetResourceRequest(url, withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "GET " + resourceClass.getName() + "/$" + name, timeoutLong); } if (result.isUnsuccessfulRequest()) { throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload()); @@ -383,7 +383,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { Bundle transactionResult = null; try { transactionResult = client.postBatchRequest(resourceAddress.getBaseServiceUri(), ByteUtils.resourceToByteArray(batch, false, isJson(getPreferredResourceFormat()), false), withVer(getPreferredResourceFormat(), "4.0"), - generateHeaders(), + generateHeaders(true), "transaction", timeoutOperation + (timeoutEntry * batch.getEntry().size())); } catch (Exception e) { handleException("An error occurred trying to process this transaction request", e); @@ -398,7 +398,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { try { result = client.issuePostRequest(resourceAddress.resolveValidateUri(resourceClass, id), ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false), - withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(), + withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), "POST " + resourceClass.getName() + (id != null ? "/" + id : "") + "/$validate", timeoutLong); if (result.isUnsuccessfulRequest()) { throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload()); @@ -458,7 +458,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { result = client.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand"), ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true), withVer(getPreferredResourceFormat(), "4.0"), - generateHeaders(), + generateHeaders(true), "ValueSet/$expand?url=" + source.getUrl(), timeoutExpand); } catch (IOException e) { @@ -476,7 +476,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { try { result = client.issueGetResourceRequest(resourceAddress.resolveOperationUri(CodeSystem.class, "lookup", params), withVer(getPreferredResourceFormat(), "4.0"), - generateHeaders(), + generateHeaders(false), "CodeSystem/$lookup", timeoutNormal); } catch (IOException e) { @@ -495,7 +495,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { result = client.issuePostRequest(resourceAddress.resolveOperationUri(CodeSystem.class, "lookup"), ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true), withVer(getPreferredResourceFormat(), "4.0"), - generateHeaders(), + generateHeaders(true), "CodeSystem/$lookup", timeoutNormal); } catch (IOException e) { @@ -514,7 +514,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { result = client.issuePostRequest(resourceAddress.resolveOperationUri(ConceptMap.class, "translate"), ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true), withVer(getPreferredResourceFormat(), "4.0"), - generateHeaders(), + generateHeaders(true), "ConceptMap/$translate", timeoutNormal); } catch (IOException e) { @@ -539,7 +539,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { result = client.issuePostRequest(resourceAddress.resolveOperationUri(null, "closure", new HashMap()), ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true), withVer(getPreferredResourceFormat(), "4.0"), - generateHeaders(), + generateHeaders(true), "Closure?name=" + name, timeoutNormal); if (result.isUnsuccessfulRequest()) { @@ -561,7 +561,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { result = client.issuePostRequest(resourceAddress.resolveOperationUri(null, "closure", new HashMap()), ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true), withVer(getPreferredResourceFormat(), "4.0"), - generateHeaders(), + generateHeaders(true), "UpdateClosure?name=" + name, timeoutOperation); if (result.isUnsuccessfulRequest()) { @@ -617,7 +617,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { this.headers = headers; } - private Headers generateHeaders() { + private Headers generateHeaders(boolean hasBody) { Headers.Builder builder = new Headers.Builder(); // Add basic auth header if it exists if (basicAuthHeaderExists()) { @@ -635,7 +635,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { builder.add("Accept-Language: "+acceptLang); } - if (!Utilities.noString(contentLang)) { + if (hasBody && !Utilities.noString(contentLang)) { builder.add("Content-Language: "+contentLang); } @@ -689,7 +689,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { org.hl7.fhir.r5.utils.client.network.ResourceRequest result = null; try { result = client.issueGetResourceRequest(resourceAddress.resolveGetResource(resourceClass, id), - withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(), resourceClass.getName()+"/"+id, timeoutNormal); + withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), resourceClass.getName()+"/"+id, timeoutNormal); } catch (IOException e) { throw new FHIRException(e); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirRequestBuilder.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirRequestBuilder.java index 58e5c9511..f57b05125 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirRequestBuilder.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirRequestBuilder.java @@ -13,6 +13,7 @@ import org.hl7.fhir.r5.utils.ResourceUtilities; import org.hl7.fhir.r5.utils.client.EFhirClientException; import org.hl7.fhir.r5.utils.client.ResourceFormat; import org.hl7.fhir.utilities.MimeType; +import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.settings.FhirSettings; import javax.annotation.Nonnull; @@ -91,7 +92,9 @@ public class FhirRequestBuilder { */ protected static void addResourceFormatHeaders(Request.Builder request, String format) { request.addHeader("Accept", format); - request.addHeader("Content-Type", format + ";charset=" + DEFAULT_CHARSET); + if (Utilities.existsInList(request.getMethod$okhttp(), "POST", "PUT")) { + request.addHeader("Content-Type", format + ";charset=" + DEFAULT_CHARSET); + } } /** diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IMessagingServices.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IMessagingServices.java new file mode 100644 index 000000000..301098562 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IMessagingServices.java @@ -0,0 +1,11 @@ +package org.hl7.fhir.r5.utils.validation; + +import java.util.List; + +import org.hl7.fhir.utilities.validation.ValidationMessage; +import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; + +public interface IMessagingServices { + ValidationMessage signpost(List errors, String ruleDate, IssueType type, int line, int col, String path, String theMessage, Object... theMessageArguments); + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IResourceValidator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IResourceValidator.java index aa465b531..691c4fa73 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IResourceValidator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IResourceValidator.java @@ -31,6 +31,7 @@ package org.hl7.fhir.r5.utils.validation; import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat; import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.StructureDefinition; @@ -53,6 +54,8 @@ import java.util.List; * */ public interface IResourceValidator { + + IWorkerContext getContext(); /** * how much to check displays for coded elements diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IValidationPolicyAdvisor.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IValidationPolicyAdvisor.java index 6f93829c8..5af265edc 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IValidationPolicyAdvisor.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IValidationPolicyAdvisor.java @@ -9,6 +9,7 @@ import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.utils.validation.constants.ContainedReferenceValidationPolicy; import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy; +import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.r5.utils.validation.constants.CodedContentValidationPolicy; import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor.ElementValidationAction; import org.hl7.fhir.r5.utils.validation.constants.BindingKind; @@ -131,5 +132,42 @@ public interface IValidationPolicyAdvisor { ValueSet valueSet, List systems); + /** + * This is called after a resource has been validated against the base structure, + * but before it's validated against any profiles specified in .meta.profile or in the parameters. + * This can be used to determine what additional profiles should be applied, for instance + * those derived from the http://hl7.org/fhir/tools/StructureDefinition/profile-mapping extension + * + * Note that the resource is an elementModel resource, not an IBaseResource. This is less convenient to + * read values from, but is the way the internals of the validator works (e.g. the version of the resource + * might be any version from R2-R6) + * + * The base implementation applies the mandatory vital signs to observations that have LOINC or SNOMED CT + * codes that indicate that they are vital signs. Note that these profiles are not optional; all vital sign resources + * are required to conform to them. For this reason, if you're providing your own policy advisor, you should + * keep a reference to the default one, or call BasePolicyAdvisorForFullValidation directly. You can choose not to, + * but if you do, you are allowing for resources that deviate from the FHIR specification (in a way that the + * community considers clinically unsafe, since it means that software (probably) will miss vital signs for + * patients). + * + * @param validator + * @param appContext What was originally provided from the app for it's context + * @param stackPath The current path for the stack. Note that the because of cross-references and FHIRPath conformsTo() statements, the stack can wind through the content unpredictably. + * @param definition the definition being validated against (might be useful: ElementDefinition.base.path, ElementDefinition.type, ElementDefinition.binding + * @param structure The structure definition that contains the element definition being validated against (may be from the base spec, may be from a profile) + * @param resource The actual resource (as an element model) so that the implementation can inspect the values in order to decide what profiles to apply + * @param valid true if the resource is so far considered valid + * @param messages all the validation messages. Implementations can inspect this, but the real purpose is to populate the messages with information messages explaining why profiles were (or weren't) applied + * @return + */ + List getImpliedProfilesForResource(IResourceValidator validator, + Object appContext, + String stackPath, + ElementDefinition definition, + StructureDefinition structure, + Element resource, + boolean valid, + IMessagingServices msgServices, + List messages); } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/client/network/FhirRequestBuilderTest.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/client/network/FhirRequestBuilderTest.java index 78ab6d45b..7be6d672a 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/client/network/FhirRequestBuilderTest.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/client/network/FhirRequestBuilderTest.java @@ -26,10 +26,27 @@ class FhirRequestBuilderTest { } @Test - @DisplayName("Test resource format headers are added correctly.") - void addResourceFormatHeaders() { + @DisplayName("Test resource format headers are added correctly (GET).") + void addResourceFormatHeadersGET() { String testFormat = "yaml"; Request.Builder request = new Request.Builder().url("http://www.google.com"); + request.setMethod$okhttp("GET"); + FhirRequestBuilder.addResourceFormatHeaders(request, testFormat); + + Map> headersMap = request.build().headers().toMultimap(); + Assertions.assertNotNull(headersMap.get("Accept"), "Accept header null."); + Assertions.assertEquals(testFormat, headersMap.get("Accept").get(0), + "Accept header not populated with expected value " + testFormat + "."); + + Assertions.assertNull(headersMap.get("Content-Type"), "Content-Type header not null."); + } + + @Test + @DisplayName("Test resource format headers are added correctly (POST).") + void addResourceFormatHeadersPOST() { + String testFormat = "yaml"; + Request.Builder request = new Request.Builder().url("http://www.google.com"); + request.setMethod$okhttp("POST"); FhirRequestBuilder.addResourceFormatHeaders(request, testFormat); Map> headersMap = request.build().headers().toMultimap(); diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/filesystem/DirectoryVisitor.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/filesystem/DirectoryVisitor.java new file mode 100644 index 000000000..27b164098 --- /dev/null +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/filesystem/DirectoryVisitor.java @@ -0,0 +1,78 @@ +package org.hl7.fhir.utilities.filesystem; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +import org.hl7.fhir.utilities.Utilities; + +public class DirectoryVisitor { + + public interface IDirectoryVisitorImplementation { + boolean enterDirectory(File directory) throws IOException; + boolean visitFile(File file) throws IOException; // return true if count + } + + private IDirectoryVisitorImplementation worker; + private Set extensions; + + public DirectoryVisitor(IDirectoryVisitorImplementation worker, Set extensions) { + super(); + this.worker = worker; + this.extensions = extensions; + if (this.extensions == null) { + extensions = new HashSet<>(); + } + } + + public DirectoryVisitor(IDirectoryVisitorImplementation worker, String... extensionList) { + super(); + this.worker = worker; + extensions = new HashSet<>(); + for (String s : extensionList) { + extensions.add(s); + } + } + + public DirectoryVisitor(IDirectoryVisitorImplementation worker) { + super(); + this.worker = worker; + extensions = new HashSet<>(); + } + + public static int visitDirectory(IDirectoryVisitorImplementation worker, String path) throws IOException { + return new DirectoryVisitor(worker).visit(path); + } + + public static int visitDirectory(IDirectoryVisitorImplementation worker, String path, Set extensions) throws IOException { + return new DirectoryVisitor(worker, extensions).visit(path); + } + + public static int visitDirectory(IDirectoryVisitorImplementation worker, String path, String... extensionList) throws IOException { + return new DirectoryVisitor(worker, extensionList).visit(path); + } + + public int visit(String path) throws IOException { + return visit(ManagedFileAccess.file(path)); + } + + private int visit(File file) throws IOException { + int count = 0; + if (file.isDirectory()) { + if (worker.enterDirectory(file)) { + for (File f : ManagedFileAccess.listFiles(file)) { + count += visit(f); + } + } + } else { + String ext = file.getName().substring(file.getName().lastIndexOf(".")+1); + if (extensions.isEmpty() || extensions.contains(ext)) { + if (worker.visitFile(file)) { + count++; + } + } + } + return count; + } +} diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/filesystem/ManagedFileAccess.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/filesystem/ManagedFileAccess.java index 2e261b9fd..0a5caae35 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/filesystem/ManagedFileAccess.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/filesystem/ManagedFileAccess.java @@ -61,6 +61,7 @@ public class ManagedFileAccess { FileInputStream inStream(String pathname); FileOutputStream outStream(String pathname); CSFile csfile(String pathname); + File[] listFiles(File file) throws IOException; // file would be returned from file() above } public enum FileAccessPolicy { @@ -222,4 +223,20 @@ public class ManagedFileAccess { } } + public static File[] listFiles(File f) throws IOException { + switch (accessPolicy) { + case DIRECT: + if (!inAllowedPaths(f.getAbsolutePath())) { + throw new IOException("The pathname '"+f.getAbsolutePath()+"' cannot be accessed by policy"); + } + return f.listFiles(); + case MANAGED: + return accessor.listFiles(f); + case PROHIBITED: + throw new IOException("Access to files is not allowed by local security policy"); + default: + throw new IOException("Internal Error"); + } + } + } \ No newline at end of file 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 152b10db8..09b7782f3 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 @@ -125,7 +125,7 @@ public abstract class I18nBase { if (Objects.nonNull(theMessageArguments) && theMessageArguments.length > 0) { message = MessageFormat.format(messages.getString(theMessage).trim(), theMessageArguments); } else { - message = messages.getString(theMessage).trim(); + message = MessageFormat.format(messages.getString(theMessage).trim(), null); } } return message; 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 c6746d1b4..2605b5627 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 @@ -1079,4 +1079,5 @@ public class I18nConstants { public static final String SD_TYPE_PARAMETER_INVALID = "SD_TYPE_PARAMETER_INVALID"; public static final String SD_TYPE_PARAMETER_INVALID_REF = "SD_TYPE_PARAMETER_INVALID_REF"; public static final String SD_TYPE_PARAM_NOT_SPECIFIED = "SD_TYPE_PARAM_NOT_SPECIFIED"; + public static final String SD_TYPE_PARAMETER_ABSTRACT_WARNING = "SD_TYPE_PARAMETER_ABSTRACT_WARNING"; } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/POGenerator.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/POGenerator.java index ac736d0c2..8a5fb35d0 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/POGenerator.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/POGenerator.java @@ -16,6 +16,8 @@ import java.util.Set; import org.hl7.fhir.utilities.StringPair; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.filesystem.DirectoryVisitor; +import org.hl7.fhir.utilities.filesystem.DirectoryVisitor.IDirectoryVisitorImplementation; import org.hl7.fhir.utilities.filesystem.ManagedFileAccess; /** @@ -123,9 +125,9 @@ public class POGenerator { } cd.defined = found; } - scanJavaSource(new File(core), consts, "RenderingI18nContext", "RenderingContext"); - scanJavaSource(new File(igpub), consts, "RenderingI18nContext", "RenderingContext"); - scanPascalSource(new File(pascal), props); + scanJavaSource(core, consts, "RenderingI18nContext", "RenderingContext"); + scanJavaSource(igpub, consts, "RenderingI18nContext", "RenderingContext"); + scanPascalSource(pascal, props); Set pns = new HashSet<>(); for (PropertyValue p : props) { @@ -170,9 +172,9 @@ public class POGenerator { cd.defined = found; } - scanJavaSource(new File(core), consts, "I18nConstants"); - scanJavaSource(new File(igpub), consts, "I18nConstants"); - scanPascalSource(new File(pascal), props); + scanJavaSource(core, consts, "I18nConstants"); + scanJavaSource(igpub, consts, "I18nConstants"); + scanPascalSource(pascal, props); pns = new HashSet<>(); for (PropertyValue p : props) { @@ -212,73 +214,86 @@ public class POGenerator { return ok; } - private boolean scanJavaSource(File file, List consts, String... names) throws FileNotFoundException, IOException { - if (file.isDirectory()) { - boolean found = true; - for (File f : file.listFiles()) { - if (!Utilities.existsInList(f.getName(), "model", "formats")) { - found = scanJavaSource(f, consts, names) && found; - } - } - return false; - } else { - String ext = file.getName().substring(file.getName().lastIndexOf(".")+1); - if ("java".equals(ext)) { - String source = TextFile.fileToString(file); - for (ConstantDefinition cd : consts) { - if (!cd.used) { - boolean found = false; - for (String n : names) { - if (source.contains(n+"."+cd.name+",")) { - found = true; - } - if (source.contains(n+"."+cd.name+")")) { - found = true; - } - if (source.contains(n+"."+cd.name+" :")) { - found = true; - } - if (source.contains(n+"."+cd.name+";")) { - found = true; - } - } - if (found) { - cd.used = true; - } - } - } - return true; - } else { - return false; - } + private class JavaScanner implements IDirectoryVisitorImplementation { + List consts; + List names; + + @Override + public boolean enterDirectory(File f) throws IOException { + return !Utilities.existsInList(f.getName(), "model", "formats"); } - } - - private void scanPascalSource(File file, List defs) throws FileNotFoundException, IOException { - if (file.isDirectory()) { - for (File f : file.listFiles()) { - scanPascalSource(f, defs); - } - } else { - String ext = file.getName().substring(file.getName().lastIndexOf(".")+1); - if ("pas".equals(ext)) { - String source = TextFile.fileToString(file); - for (PropertyValue pv : defs) { - if (!pv.used) { - boolean found = false; - String pn = pv.getBaseName(); - if (source.contains("'"+pn+"'")) { + @Override + public boolean visitFile(File file) throws IOException { + String source = TextFile.fileToString(file); + for (ConstantDefinition cd : consts) { + if (!cd.used) { + boolean found = false; + for (String n : names) { + if (source.contains(n+"."+cd.name+",")) { found = true; } - if (found) { - pv.used = true; - } + if (source.contains(n+"."+cd.name+")")) { + found = true; + } + if (source.contains(n+"."+cd.name+" :")) { + found = true; + } + if (source.contains(n+"."+cd.name+";")) { + found = true; + } + } + if (found) { + cd.used = true; + } + } + } + return true; + } + } + + private void scanJavaSource(String path, List consts, String... names) throws FileNotFoundException, IOException { + JavaScanner scanner = new JavaScanner(); + scanner.consts = consts; + scanner.names = new ArrayList(); + for (String s : names) { + scanner.names.add(s); + } + DirectoryVisitor.visitDirectory(scanner, path, "java"); + } + + private class PascalScanner implements IDirectoryVisitorImplementation { + private List defs; + + @Override + public boolean enterDirectory(File directory) throws IOException { + return true; + } + + @Override + public boolean visitFile(File file) throws IOException { + String source = TextFile.fileToString(file); + for (PropertyValue pv : defs) { + if (!pv.used) { + boolean found = false; + String pn = pv.getBaseName(); + if (source.contains("'"+pn+"'")) { + found = true; + } + if (found) { + pv.used = true; } } } + return true; } } + + private void scanPascalSource(String path, List defs) throws FileNotFoundException, IOException { + PascalScanner scanner = new PascalScanner(); + scanner.defs = defs; + DirectoryVisitor.visitDirectory(scanner, path, "pas"); + } private List loadConstants(String path) throws FileNotFoundException, IOException { @@ -438,7 +453,7 @@ public class POGenerator { } } } else { - // we don't care; nothing to do + o.oldMsgId = null; } } else if (mode == 1) { if (!value.equals(o.msgid)) { @@ -451,7 +466,7 @@ public class POGenerator { o.msgstr.set(0, "!!"+o.msgstr.get(0)); } } else { - // we don't care; nothing to do + o.oldMsgId = null; } } else if (mode == 2) { if (!value.equals(o.msgidPlural)) { @@ -464,7 +479,7 @@ public class POGenerator { o.msgstr.set(1, "!!"+o.msgstr.get(1)); } } else { - // we don't care; nothing to do + o.oldMsgId = null; } } } diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index d8e5462c1..527bc80b7 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -20,7 +20,7 @@ BUNDLE_BUNDLE_ENTRY_FOUND_MULTIPLE_FRAGMENT = Found {0} matches for fragment {2} BUNDLE_BUNDLE_ENTRY_FULLURL_REQUIRED = Except for transactions and batches, each entry in a Bundle must have a fullUrl which is the identity of the resource in the entry BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_MULTIPLE_MATCHES = The {1} resource matched more than one of the allowed profiles ({3}) BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_NO_MATCH = The {1} resource did not match any of the allowed profiles (Type {2}: {3}) -BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_NO_MATCH_REASON = The {1} resource did not math the profile {2} because: {3} +BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_NO_MATCH_REASON = The {1} resource did not match the profile {2} because: {3} BUNDLE_BUNDLE_ENTRY_NOTFOUND_APPARENT_one = Can''t find ''{1}'' in the bundle ({2}). Note that there is a resource in the bundle with the same type and id, but it does not match because of the fullUrl based rules around matching relative references (must be ``{3}``) BUNDLE_BUNDLE_ENTRY_NOTFOUND_APPARENT_other = Can''t find ''{1}'' in the bundle ({2}). Note that there are {0} resources in the bundle with the same type and id, but they do not match because of the fullUrl based rules around matching relative references (one of ``{3}``) BUNDLE_BUNDLE_ENTRY_NOTFOUND_FRAGMENT = Can''t find ''{0}'' in the bundle ({1}) @@ -1107,3 +1107,4 @@ SD_TYPE_PARAMETER_UNKNOWN = The type definition ''{0}'' has the type parameter ' SD_TYPE_PARAMETER_INVALID = The type definition ''{2}'' has a type parameter ''{3}'', which is not consistent with it''s ancestor type definition ''{0}'' which has the type parameter ''{1}'' SD_TYPE_PARAMETER_INVALID_REF = The type ''{0}'' is a reference to ''{1}'' which has a type parameter ''{2}'' with a base type of {3} but the type parameter provided is ''{4}'' which is not the right type SD_TYPE_PARAM_NOT_SPECIFIED = The type ''{0}'' at {3} is a reference to ''{1}'' which needs a type parameter ''{2}'' but a type parameter is not provided for ''{2}'' +SD_TYPE_PARAMETER_ABSTRACT_WARNING = The type ''{0}'' at {3} refers to the abstract type ''{1}'' but the context is not an abstract type - this is usually an error diff --git a/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties b/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties index d6c6e023d..4d957579f 100644 --- a/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties +++ b/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties @@ -132,7 +132,7 @@ CAPABILITY_TYPS = Types CAPABILITY_TYP_PRES = ype are only present if at least one of the resources has support for them. CAPABILITY_UPDATE_INT = PUT a new resource version (update interaction) CAPABILITY_VREAD_INT = GET past versions of resources (vread interaction) -CAPABILTY_ALLOW_CAP = Any FHIR capability may be 'allowed' by the system unless explicitly marked as 'SHALL NOT'. A few items are marked as MAY in the Implementation Guide to highlight their potential relevance to the use case. +CAPABILTY_ALLOW_CAP = Any FHIR capability may be ''allowed'' by the system unless explicitly marked as ''SHALL NOT''. A few items are marked as MAY in the Implementation Guide to highlight their potential relevance to the use case. CAPABILTY_SHALL_SUPP = SHALL Support the Following Implementation Guides CODESYSTEM_CONCEPTS = Concepts CODESYSTEM_CONTENT_COMPLETE = This code system defines the following codes: @@ -537,10 +537,10 @@ GENERAL_MODIFIERS = Modifiers SEARCH_PAR_MULTIPLES = Multiples SEARCH_PAR_MULTIPLE_AND_APPEAR = multipleAnd: The parameter may only appear once SEARCH_PAR_MULTIPLE_AND_REPEAT = multipleAnd: The parameter may repeat in order to specify multiple values that must all be true -SEARCH_PAR_MULTIPLE_AND_SERVER = multipleAnd: It's up to the server whether the parameter may repeat in order to specify multiple values that must all be true +SEARCH_PAR_MULTIPLE_AND_SERVER = multipleAnd: It''s up to the server whether the parameter may repeat in order to specify multiple values that must all be true SEARCH_PAR_MULTIPLE_OR_MULTIPLE = multipleOr: The parameter may have multiple values (separated by comma) where at least one must be true SEARCH_PAR_MULTIPLE_OR_ONE = multipleOr: The parameter may only have one value (no comma separators) -SEARCH_PAR_MULTIPLE_OR_SERVER = multipleOr: It's up to the server whether the parameter can have multiple values (separated by comma) where at least one must be true +SEARCH_PAR_MULTIPLE_OR_SERVER = multipleOr: It''s up to the server whether the parameter can have multiple values (separated by comma) where at least one must be true SEARCH_PAR_NONE = (none) SEARCH_PAR_PROC = Processing Mode SEARCH_PAR_REND_TARGET = Target Resources diff --git a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/i18n/I18nBaseTest.java b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/i18n/I18nBaseTest.java index f19b0eea9..6259c9bd9 100644 --- a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/i18n/I18nBaseTest.java +++ b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/i18n/I18nBaseTest.java @@ -109,6 +109,20 @@ class I18nBaseTest { assertEquals("Documents oder Messages müssen mindestens einen Eintrag enthalten", result); } + @Test + @DisplayName("Test double single quotes behaviour.") + void testDoubleSingleQuotes() { + I18nTestClass testClass = new I18nTestClass(); + testClass.setLocale(Locale.US); + String actualMessageA = testClass.formatMessage(I18nConstants.VALUESET_EXAMPLE_SYSTEM_HINT, "Mazooma"); + assertEquals("Example System 'Mazooma' specified, so Concepts and Filters can't be checked", actualMessageA); + + String actualMessageB = testClass.formatMessage(I18nConstants.VALUESET_NO_SYSTEM_WARNING); + System.out.println(actualMessageB); + assertEquals("No System specified, so Concepts and Filters can't be checked", actualMessageB); + } + + @Test @DisplayName("Test German localization file contains no umlauts.") void testThatNoOneHasMessedWithTheGermans() { 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 1701bc162..cee1ebf8f 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 @@ -67,6 +67,7 @@ import org.hl7.fhir.r5.terminologies.ValueSetUtilities; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.r5.utils.XVerExtensionManager.XVerExtensionStatus; +import org.hl7.fhir.r5.utils.validation.IMessagingServices; import org.hl7.fhir.r5.utils.validation.IResourceValidator; import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher; import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier.IValidationContextResourceLoader; @@ -87,7 +88,7 @@ import org.hl7.fhir.validation.cli.utils.ValidationLevel; import org.hl7.fhir.validation.instance.utils.IndexedElement; import org.hl7.fhir.validation.instance.utils.NodeStack; -public class BaseValidator implements IValidationContextResourceLoader { +public class BaseValidator implements IValidationContextResourceLoader, IMessagingServices { public static class BooleanHolder { private boolean value = true; @@ -411,7 +412,7 @@ public class BaseValidator implements IValidationContextResourceLoader { return thePass; } - protected ValidationMessage signpost(List errors, String ruleDate, IssueType type, int line, int col, String path, String theMessage, Object... theMessageArguments) { + public ValidationMessage signpost(List errors, String ruleDate, IssueType type, int line, int col, String path, String theMessage, Object... theMessageArguments) { String message = context.formatMessage(theMessage, theMessageArguments); return addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.INFORMATION, theMessage).setSignpost(true); } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java index 895bbd2b2..2752c307a 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java @@ -67,6 +67,7 @@ import org.hl7.fhir.r5.utils.EOperationOutcome; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities; import org.hl7.fhir.r5.utils.validation.BundleValidationRule; +import org.hl7.fhir.r5.utils.validation.IMessagingServices; import org.hl7.fhir.r5.utils.validation.IResourceValidator; import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor; import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher; @@ -104,6 +105,7 @@ import org.hl7.fhir.validation.cli.utils.ProfileLoader; import org.hl7.fhir.validation.cli.utils.QuestionnaireMode; import org.hl7.fhir.validation.cli.utils.SchemaValidator; import org.hl7.fhir.validation.cli.utils.ValidationLevel; +import org.hl7.fhir.validation.instance.BasePolicyAdvisorForFullValidation; import org.hl7.fhir.validation.instance.InstanceValidator; import org.hl7.fhir.validation.instance.utils.ValidationContext; import org.hl7.fhir.utilities.ByteProvider; @@ -1253,4 +1255,12 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP return res; } + @Override + public List getImpliedProfilesForResource(IResourceValidator validator, Object appContext, + String stackPath, ElementDefinition definition, StructureDefinition structure, Element resource, boolean valid, + IMessagingServices msgServices, List messages) { + return new BasePolicyAdvisorForFullValidation().getImpliedProfilesForResource(validator, appContext, stackPath, + definition, structure, resource, valid, msgServices, messages); + } + } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java index c25ddbe95..6d93f182f 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java @@ -42,11 +42,12 @@ import org.hl7.fhir.utilities.json.parser.JsonParser; import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.validation.cli.utils.Common; +import org.hl7.fhir.validation.instance.BasePolicyAdvisorForFullValidation; import javax.annotation.Nonnull; -public class StandAloneValidatorFetcher implements IValidatorResourceFetcher, IValidationPolicyAdvisor, IWorkerContextManager.ICanonicalResourceLocator { +public class StandAloneValidatorFetcher extends BasePolicyAdvisorForFullValidation implements IValidatorResourceFetcher, IValidationPolicyAdvisor, IWorkerContextManager.ICanonicalResourceLocator { List mappingsUris = new ArrayList<>(); private FilesystemPackageCacheManager pcm; @@ -75,31 +76,6 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher, IV String url) { return ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS; } - - @Override - public ContainedReferenceValidationPolicy policyForContained(IResourceValidator validator, - Object appContext, - StructureDefinition structure, - ElementDefinition element, - String containerType, - String containerId, - Element.SpecialElement containingResourceType, - String path, - String url) { - return ContainedReferenceValidationPolicy.CHECK_VALID; - } - - @Override - public EnumSet policyForResource(IResourceValidator validator, Object appContext, - StructureDefinition type, String path) { - return EnumSet.allOf(ResourceValidationAction.class); - } - - @Override - public EnumSet policyForElement(IResourceValidator validator, Object appContext, - StructureDefinition structure, ElementDefinition element, String path) { - return EnumSet.allOf(ElementValidationAction.class); - } @Override public boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type, boolean canonical) throws IOException, FHIRException { @@ -316,19 +292,6 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher, IV } } - @Override - public EnumSet policyForCodedContent(IResourceValidator validator, - Object appContext, - String stackPath, - ElementDefinition definition, - StructureDefinition structure, - BindingKind kind, - AdditionalBindingPurpose purpose, - ValueSet valueSet, - List systems) { - return EnumSet.allOf(CodedContentValidationAction.class); - } - @Override public Set fetchCanonicalResourceVersions(IResourceValidator validator, Object appContext, String url) { return new HashSet<>(); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/BasePolicyAdvisorForFullValidation.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/BasePolicyAdvisorForFullValidation.java new file mode 100644 index 000000000..0b012b23e --- /dev/null +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/BasePolicyAdvisorForFullValidation.java @@ -0,0 +1,166 @@ +package org.hl7.fhir.validation.instance; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; + +import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.elementmodel.Element; +import org.hl7.fhir.r5.elementmodel.Element.SpecialElement; +import org.hl7.fhir.r5.model.ElementDefinition; +import org.hl7.fhir.r5.model.StructureDefinition; +import org.hl7.fhir.r5.model.ValueSet; +import org.hl7.fhir.r5.utils.validation.IMessagingServices; +import org.hl7.fhir.r5.utils.validation.IResourceValidator; +import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor; +import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor.AdditionalBindingPurpose; +import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor.CodedContentValidationAction; +import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor.ElementValidationAction; +import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor.ResourceValidationAction; +import org.hl7.fhir.r5.utils.validation.constants.BindingKind; +import org.hl7.fhir.r5.utils.validation.constants.ContainedReferenceValidationPolicy; +import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy; +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; + +public class BasePolicyAdvisorForFullValidation implements IValidationPolicyAdvisor { + + @Override + public ReferenceValidationPolicy policyForReference(IResourceValidator validator, Object appContext, String path, String url) { + return ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS; + } + + @Override + public ContainedReferenceValidationPolicy policyForContained(IResourceValidator validator, Object appContext, + StructureDefinition structure, ElementDefinition element, String containerType, String containerId, + SpecialElement containingResourceType, String path, String url) { + + return ContainedReferenceValidationPolicy.CHECK_VALID; + } + + @Override + public EnumSet policyForResource(IResourceValidator validator, Object appContext, + StructureDefinition type, String path) { + return EnumSet.allOf(ResourceValidationAction.class); + } + + @Override + public EnumSet policyForElement(IResourceValidator validator, Object appContext, + StructureDefinition structure, ElementDefinition element, String path) { + return EnumSet.allOf(ElementValidationAction.class); + } + + @Override + public EnumSet policyForCodedContent(IResourceValidator validator, + Object appContext, + String stackPath, + ElementDefinition definition, + StructureDefinition structure, + BindingKind kind, + AdditionalBindingPurpose purpose, + ValueSet valueSet, + List systems) { + return EnumSet.allOf(CodedContentValidationAction.class); + } + + @Override + public List getImpliedProfilesForResource(IResourceValidator validator, Object appContext, + String stackPath, ElementDefinition definition, StructureDefinition structure, Element resource, boolean valid, + IMessagingServices msgServices, List messages) { + List profiles = new ArrayList(); + if ("Observation".equals(resource.fhirType()) && VersionUtilities.isR4Plus(validator.getContext().getVersion())) { + getImpliedProfilesForObservation(profiles, msgServices, messages, validator.getContext(), stackPath, resource); + } + return profiles; + } + + + private void getImpliedProfilesForObservation(List profiles, IMessagingServices msgServices, List messages, IWorkerContext context, String stackPath, Element resource) { + Element code = resource.getNamedChild("code", false); + List codes = new ArrayList<>(); + if (hasLoincCode(code, codes, "85353-1")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/vitalspanel", "Vital Signs Panel", "LOINC", codes); + } else if (hasLoincCode(code, codes, "9279-1", "76170-0", "76172-6", "76171-8", "19840-8", "33438-3", "76270-8", "11291-2")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/resprate", "Respiratory Rate", "LOINC", codes); + } else if (hasLoincCode(code, codes, "60978-4", "73795-7", "73799-9", "76476-1", "76477-9", "8867-4", "8889-8", "8890-6", "8891-4", "8892-2", "8893-0", "40443-4", "55425-3", "68999-2", "11328-2", "69000-8", "69000-8", "60978-4", "60978-4", "8890-6", "8886-4", "68999-2", "68999-2")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/heartrate", "Heart rate", "LOINC", codes); + } else if (hasLoincCode(code, codes, "2708-6", "19224-5", "20564-1", "2709-4", "2710-2", "2713-6", "51733-4", "59408-5", "59417-6", "89276-0", "97549-0")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/oxygensat", "Oxygen saturation", "LOINC", codes); + } else if (hasLoincCode(code, codes, "8310-5", "60834-9", "60835-6", "60836-4", "60838-0", "60955-2", "61009-7", "75539-7", "75987-8", "76010-8", "76011-6", "76278-1", "8309-7", "8310-5", "8328-7", "8329-5", "8330-3", "8331-1", "8332-9", "8333-7", "8334-5", "91371-5", "98657-0", "98663-8")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/bodytemp", "Body temperature", "LOINC", codes); + } else if (hasLoincCode(code, codes, "8302-2", "3137-7", "3138-5", "8302-2", "8306-3", "8308-9")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/bodyheight", "Body height", "LOINC", codes); + } else if (hasLoincCode(code, codes, "9843-4", "8287-5", "9843-4")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/headcircum", "Head circumference", "LOINC", codes); + } else if (hasLoincCode(code, codes, "29463-7", "29463-7", "3141-9", "3142-7", "75292-3", "79348-9", "8350-1", "8351-9")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/bodyweight", "Body weight", "LOINC", codes); + } else if (hasLoincCode(code, codes, "39156-5", "39156-5", "59574-4", "89270-3")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/bmi", "Body mass index", "LOINC", codes); + } else if (hasLoincCode(code, codes, "85354-9", "35094-2", "8459-0", "85354-9", "76534-7", "55284-4", "8480-6")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/bp", "Blood pressure systolic and diastolic", "LOINC", codes); + + } else if (hasSctCode(code, codes, "46680005")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/vitalspanel", "Vital Signs Panel", "SNOMED CT", codes); + } else if (hasSctCode(code, codes, "86290005", "271625008", "271306003")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/bp", "Blood pressure systolic and diastolic", "SNOMED CT", codes); + } else if (hasSctCode(code, codes, "271306003", "249043002", "444981005", "399017001", "251670001", "429525003", "429614003")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/heartrate", "Heart rate", "SNOMED CT", codes); + } else if (hasSctCode(code, codes, "103228002", "103228002", "442349007", "442476006", "442440005", "431314004", "442734002", "713194001")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/oxygensat", "Oxygen saturation", "SNOMED CT", codes); + } else if (hasSctCode(code, codes, "386725007", "276885007", "300076005", "1222808002", "364246006", "307047009", "708499008", "708499008", "431598003", "698831002", "698832009", "415882003", "415974002", "415929009", "415945006")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/bodytemp", "Body temperature", "SNOMED CT", codes); + } else if (hasSctCode(code, codes, "1153637007", "1162419008", "50373000", "1162418000", "1230278008", "1162392001", "1162417005")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/bodyheight", "Body height", "SNOMED CT", codes); + } else if (hasSctCode(code, codes, "363812007", "169876006", "1269262007", "363811000")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/headcircum", "Head circumference", "SNOMED CT", codes); + } else if (hasSctCode(code, codes, "363811000", "60621009", "735395000", "425024002", "424927000", "784399000", "1162416001", "1162415002")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/bodyweight", "Body weight", "SNOMED CT", codes); + } else if (hasSctCode(code, codes, "60621009")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/bmi", "Body mass index", "SNOMED CT", codes); + } else if (hasSctCode(code, codes, "75367002", "251076008", "163033001", "163035008", "386534000", "386536003", "271649006", "271649006", "271650006", "407556006", "407554009", "716579001", "399304008")) { + addProfile(profiles, msgServices, messages, context, stackPath, resource, "http://hl7.org/fhir/StructureDefinition/bp", "Blood pressure systolic and diastolic", "SNOMED CT", codes); + } + + } + + private void addProfile(List profiles, IMessagingServices msgServices, List messages, IWorkerContext context, String stackPath, Element resource, String url, String name, String systemName, List codes) { + resource.addMessage(msgServices.signpost(messages, null, IssueType.INFORMATIONAL, resource.line(), resource.col(), stackPath, I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_OBS, url, name, systemName, codes.get(0))); + StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); + if (sd != null) { + profiles.add(sd); + } else { + // complain? + } + } + + protected boolean hasLoincCode(Element code, List codes, String... values) { + if (code != null) { + List codings = code.getChildren("coding"); + for (Element coding : codings) { + if ("http://loinc.org".equals(coding.getNamedChildValue("system", false)) && Utilities.existsInList(coding.getNamedChildValue("code", false), values)) { + codes.add(coding.getNamedChildValue("code", false)); + return true; + } + } + } + return false; + } + + protected boolean hasSctCode(Element code, List codes, String... values) { + if (code != null) { + List codings = code.getChildren("coding"); + for (Element coding : codings) { + if ("http://snomed.info/sct".equals(coding.getNamedChildValue("system", false)) && Utilities.existsInList(coding.getNamedChildValue("code", false), values)) { + codes.add(coding.getNamedChildValue("code", false)); + return true; + } + } + } + return false; + } + + +} 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 924981228..be75b0df8 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 @@ -173,6 +173,7 @@ import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.r5.utils.XVerExtensionManager.XVerExtensionStatus; import org.hl7.fhir.r5.utils.sql.Validator; import org.hl7.fhir.r5.utils.validation.BundleValidationRule; +import org.hl7.fhir.r5.utils.validation.IMessagingServices; import org.hl7.fhir.r5.utils.validation.IResourceValidator; import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor; import org.hl7.fhir.r5.utils.validation.IValidationProfileUsageTracker; @@ -595,7 +596,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private boolean noBindingMsgSuppressed; private Map fetchCache = new HashMap<>(); private HashMap resourceTracker = new HashMap<>(); - private IValidationPolicyAdvisor policyAdvisor; + private IValidationPolicyAdvisor policyAdvisor = new BasePolicyAdvisorForFullValidation(); long time = 0; long start = 0; long lastlog = 0; @@ -691,6 +692,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat @Override public IResourceValidator setPolicyAdvisor(IValidationPolicyAdvisor advisor) { + if (advisor == null) { + throw new Error("Cannot set advisor to null"); + } this.policyAdvisor = advisor; return this; } @@ -3111,7 +3115,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } if (!found) { if (type.equals("canonical")) { - ReferenceValidationPolicy rp = policyAdvisor == null ? ReferenceValidationPolicy.CHECK_VALID : policyAdvisor.policyForReference(this, valContext, path, url); + ReferenceValidationPolicy rp = policyAdvisor.policyForReference(this, valContext, path, url); if (rp == ReferenceValidationPolicy.CHECK_EXISTS || rp == ReferenceValidationPolicy.CHECK_EXISTS_AND_TYPE) { ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE, url) && ok; } else { @@ -3128,7 +3132,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } else { if (type.equals("canonical")) { - ReferenceValidationPolicy rp = policyAdvisor == null ? ReferenceValidationPolicy.CHECK_VALID : policyAdvisor.policyForReference(this, valContext, path, url); + ReferenceValidationPolicy rp = policyAdvisor.policyForReference(this, valContext, path, url); if (rp == ReferenceValidationPolicy.CHECK_EXISTS_AND_TYPE || rp == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS || rp == ReferenceValidationPolicy.CHECK_VALID) { try { Resource r = null; @@ -3581,8 +3585,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat ok = false; } } else { - EnumSet validationPolicy = getPolicyAdvisor() == null ? - EnumSet.allOf(CodedContentValidationAction.class) : getPolicyAdvisor().policyForCodedContent(this, valContext, stack.getLiteralPath(), elementContext, profile, BindingKind.PRIMARY, null, vs, new ArrayList<>()); + EnumSet validationPolicy = policyAdvisor.policyForCodedContent(this, valContext, stack.getLiteralPath(), elementContext, profile, BindingKind.PRIMARY, null, vs, new ArrayList<>()); if (!validationPolicy.isEmpty()) { long t = System.nanoTime(); @@ -3918,11 +3921,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (refType.equals("contained") || refType.equals("bundled")) { pol = ReferenceValidationPolicy.CHECK_VALID; } else { - if (policyAdvisor == null) { - pol = ReferenceValidationPolicy.IGNORE; - } else { - pol = policyAdvisor.policyForReference(this, valContext.getAppContext(), path, ref); - } + pol = policyAdvisor.policyForReference(this, valContext.getAppContext(), path, ref); } if (conditional) { @@ -5705,15 +5704,22 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat ok = false; } if (checkSpecials) { - ok = checkSpecials(valContext, errors, element, stack, checkSpecials, pct, mode, fromContained) && ok; + ok = checkSpecials(valContext, errors, element, stack, checkSpecials, pct, mode, fromContained, ok) && 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, boolean contained) { + public boolean checkSpecials(ValidationContext valContext, List errors, Element element, NodeStack stack, boolean checkSpecials, PercentageTracker pct, ValidationMode mode, boolean contained, boolean isOk) { boolean ok = true; + // first, does the policy advisor have profiles it wants us to check? + List profiles = policyAdvisor.getImpliedProfilesForResource(this, valContext.getAppContext(), stack.getLiteralPath(), + element.getProperty().getDefinition(), element.getProperty().getStructure(), element, isOk, this, errors); + for (StructureDefinition sd : profiles) { + ok = startInner(valContext, errors, element, element, sd, stack, false, pct, mode, false) && ok; + } + long t = System.nanoTime(); try { if (VersionUtilities.getCanonicalResourceNames(context.getVersion()).contains(element.getType())) { @@ -5966,8 +5972,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else { SpecialElement special = element.getSpecial(); - ContainedReferenceValidationPolicy containedValidationPolicy = getPolicyAdvisor() == null ? - ContainedReferenceValidationPolicy.CHECK_VALID : getPolicyAdvisor().policyForContained(this, + ContainedReferenceValidationPolicy containedValidationPolicy = policyAdvisor.policyForContained(this, valContext, parentProfile, child, context.fhirType(), context.getId(), special, path, parentProfile.getUrl()); if (containedValidationPolicy.ignore()) { @@ -6021,7 +6026,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } - checkSpecials(valContext, errors, element, stack, ok, pct, mode, true); + checkSpecials(valContext, errors, element, stack, ok, pct, mode, true, ok); if (typeForResource.getProfile().size() == 1) { long t = System.nanoTime(); @@ -6427,8 +6432,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (debug) { System.out.println(" check " + localStack.getLiteralPath()+" against "+ei.getDefinition().getId()+" in profile "+profile.getVersionedUrl()+time()); } - EnumSet actionSet = policyAdvisor == null ? EnumSet.allOf(ElementValidationAction.class) : - policyAdvisor.policyForElement(this, valContext.getAppContext(), profile, ei.getDefinition(), localStack.getLiteralPath()); + EnumSet actionSet = policyAdvisor.policyForElement(this, valContext.getAppContext(), profile, ei.getDefinition(), localStack.getLiteralPath()); String localStackLiteralPath = localStack.getLiteralPath(); String eiPath = ei.getPath(); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/BundleValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/BundleValidator.java index 64fba121f..a4ec1a17d 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/BundleValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/BundleValidator.java @@ -181,7 +181,7 @@ public class BundleValidator extends BaseValidator { } } // also, while we're here, check the specials, since this doesn't happen anywhere else - ((InstanceValidator) parent).checkSpecials(hostContext, errors, res, rstack, true, pct, mode, true); + ((InstanceValidator) parent).checkSpecials(hostContext, errors, res, rstack, true, pct, mode, true, ok); } // todo: check specials diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ObservationValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ObservationValidator.java index a0f6db477..878aeee0d 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ObservationValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ObservationValidator.java @@ -38,92 +38,8 @@ public class ObservationValidator extends BaseValidator { element.getNamedChild("effectiveTiming", false) != null || element.getNamedChild("effectiveInstant", false) != null, I18nConstants.ALL_OBSERVATIONS_SHOULD_HAVE_AN_EFFECTIVEDATETIME_OR_AN_EFFECTIVEPERIOD, element.getProperty().typeSummary()) && ok; - // hook in the vital signs - if (VersionUtilities.isR4Plus(context.getVersion())) { - Element code = element.getNamedChild("code", false); - List codes = new ArrayList<>(); - if (hasLoincCode(code, codes, "85353-1")) { - ok = checkObservationAgainstProfile(valContext, errors, element, stack, "http://hl7.org/fhir/StructureDefinition/vitalspanel", "Vital Signs Panel", "LOINC", codes, pct, mode) && ok; - } else if (hasLoincCode(code, codes, "9279-1", "76170-0", "76172-6", "76171-8", "19840-8", "33438-3", "76270-8", "11291-2")) { - ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/resprate", "Respiratory Rate", "LOINC", codes, pct, mode) && ok; - } else if (hasLoincCode(code, codes, "60978-4", "73795-7", "73799-9", "76476-1", "76477-9", "8867-4", "8889-8", "8890-6", "8891-4", "8892-2", "8893-0", "40443-4", "55425-3", "68999-2", "11328-2", "69000-8", "69000-8", "60978-4", "60978-4", "8890-6", "8886-4", "68999-2", "68999-2")) { - ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/heartrate", "Heart rate", "LOINC", codes, pct, mode) && ok; - } else if (hasLoincCode(code, codes, "2708-6", "19224-5", "20564-1", "2709-4", "2710-2", "2713-6", "51733-4", "59408-5", "59417-6", "89276-0", "97549-0")) { - ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/oxygensat", "Oxygen saturation", "LOINC", codes, pct, mode) && ok; - } else if (hasLoincCode(code, codes, "8310-5", "60834-9", "60835-6", "60836-4", "60838-0", "60955-2", "61009-7", "75539-7", "75987-8", "76010-8", "76011-6", "76278-1", "8309-7", "8310-5", "8328-7", "8329-5", "8330-3", "8331-1", "8332-9", "8333-7", "8334-5", "91371-5", "98657-0", "98663-8")) { - ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bodytemp", "Body temperature", "LOINC", codes, pct, mode) && ok; - } else if (hasLoincCode(code, codes, "8302-2", "3137-7", "3138-5", "8302-2", "8306-3", "8308-9")) { - ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bodyheight", "Body height", "LOINC", codes, pct, mode) && ok; - } else if (hasLoincCode(code, codes, "9843-4", "8287-5", "9843-4")) { - ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/headcircum", "Head circumference", "LOINC", codes, pct, mode) && ok; - } else if (hasLoincCode(code, codes, "29463-7", "29463-7", "3141-9", "3142-7", "75292-3", "79348-9", "8350-1", "8351-9")) { - ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bodyweight", "Body weight", "LOINC", codes, pct, mode) && ok; - } else if (hasLoincCode(code, codes, "39156-5", "39156-5", "59574-4", "89270-3")) { - ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bmi", "Body mass index", "LOINC", codes, pct, mode) && ok; - } else if (hasLoincCode(code, codes, "85354-9", "35094-2", "8459-0", "85354-9", "76534-7", "55284-4", "8480-6")) { - ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bp", "Blood pressure systolic and diastolic", "LOINC", codes, pct, mode) && ok; - - } else if (hasSctCode(code, codes, "46680005")) { - ok = checkObservationAgainstProfile(valContext, errors, element, stack, "http://hl7.org/fhir/StructureDefinition/vitalspanel", "Vital Signs Panel", "SNOMED CT", codes, pct, mode) && ok; - } else if (hasSctCode(code, codes, "86290005", "271625008", "271306003")) { - ok = checkObservationAgainstProfile(valContext,errors, element, stack, "Blood pressure systolic and diastolic", "Respiratory Rate", "SNOMED CT", codes, pct, mode) && ok; - } else if (hasSctCode(code, codes, "271306003", "249043002", "444981005", "399017001", "251670001", "429525003", "429614003")) { - ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/heartrate", "Heart rate", "SNOMED CT", codes, pct, mode) && ok; - } else if (hasSctCode(code, codes, "103228002", "103228002", "442349007", "442476006", "442440005", "431314004", "442734002", "713194001")) { - ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/oxygensat", "Oxygen saturation", "SNOMED CT", codes, pct, mode) && ok; - } else if (hasSctCode(code, codes, "386725007", "276885007", "300076005", "1222808002", "364246006", "307047009", "708499008", "708499008", "431598003", "698831002", "698832009", "415882003", "415974002", "415929009", "415945006")) { - ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bodytemp", "Body temperature", "SNOMED CT", codes, pct, mode) && ok; - } else if (hasSctCode(code, codes, "1153637007", "1162419008", "50373000", "1162418000", "1230278008", "1162392001", "1162417005")) { - ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bodyheight", "Body height", "SNOMED CT", codes, pct, mode) && ok; - } else if (hasSctCode(code, codes, "363812007", "169876006", "1269262007", "363811000")) { - ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/headcircum", "Head circumference", "SNOMED CT", codes, pct, mode) && ok; - } else if (hasSctCode(code, codes, "363811000", "60621009", "735395000", "425024002", "424927000", "784399000", "1162416001", "1162415002")) { - ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bodyweight", "Body weight", "SNOMED CT", codes, pct, mode) && ok; - } else if (hasSctCode(code, codes, "60621009")) { - ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bmi", "Body mass index", "SNOMED CT", codes, pct, mode) && ok; - } else if (hasSctCode(code, codes, "75367002", "251076008", "163033001", "163035008", "386534000", "386536003", "271649006", "271649006", "271650006", "407556006", "407554009", "716579001", "399304008")) { - ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bp", "Blood pressure systolic and diastolic", "SNOMED CT", codes, pct, mode) && ok; - } - } + // Looking for the vital signs code? It's moved to BasePolicyAdvisorForFullValidation.getImpliedProfilesForObservation return ok; } - private boolean checkObservationAgainstProfile(ValidationContext valContext, List errors, Element element, NodeStack stack, String url, String name, String sys, List loinc, PercentageTracker pct, ValidationMode mode) { - element.addMessage(signpost(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_OBS, url, name, sys, loinc.get(0))); - StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); - if (sd == null) { - return false; - } else { - return ((InstanceValidator) parent).startInner(valContext, errors, element, element, sd, stack, false, pct, mode, false); - } - } - - private boolean hasLoincCode(Element code, List codes, String... values) { - if (code != null) { - List codings = code.getChildren("coding"); - for (Element coding : codings) { - if ("http://loinc.org".equals(coding.getNamedChildValue("system", false)) && Utilities.existsInList(coding.getNamedChildValue("code", false), values)) { - codes.add(coding.getNamedChildValue("code", false)); - return true; - } - } - } - return false; - } - - private boolean hasSctCode(Element code, List codes, String... values) { - if (code != null) { - List codings = code.getChildren("coding"); - for (Element coding : codings) { - if ("http://snomed.info/sct".equals(coding.getNamedChildValue("system", false)) && Utilities.existsInList(coding.getNamedChildValue("code", false), values)) { - codes.add(coding.getNamedChildValue("code", false)); - return true; - } - } - } - return false; - } - - - } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java index 0e13c0139..09f0be183 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java @@ -14,6 +14,7 @@ import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_50; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_14_50; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; +import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; import org.hl7.fhir.r5.elementmodel.Element; @@ -87,6 +88,7 @@ public class StructureDefinitionValidator extends BaseValidator { StructureDefinition base = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition()); if (warning(errors, NO_RULE_DATE, IssueType.NOTFOUND, stack.getLiteralPath(), base != null, I18nConstants.UNABLE_TO_FIND_BASE__FOR_, sd.getBaseDefinition(), "StructureDefinition, so can't check the differential")) { if (rule(errors, NO_RULE_DATE, IssueType.NOTFOUND, stack.getLiteralPath(), sd.hasDerivation(), I18nConstants.SD_MUST_HAVE_DERIVATION, sd.getUrl())) { + checkTypeParameters(errors, stack, base, sd); boolean bok = base.getAbstract() || sd.hasKind() && sd.getKind() == base.getKind(); ok = rule(errors, "2022-11-02", IssueType.NOTFOUND, stack.getLiteralPath(), bok, I18nConstants.SD_CONSTRAINED_KIND_NO_MATCH, sd.getKind().toCode(), base.getKind().toCode(), base.getType(), base.getUrl()) && ok; if (sd.getDerivation() == TypeDerivationRule.CONSTRAINT) { @@ -193,6 +195,27 @@ public class StructureDefinitionValidator extends BaseValidator { return ok; } + private boolean checkTypeParameters(List errors, NodeStack stack, StructureDefinition base, StructureDefinition derived) { + String bt = ToolingExtensions.readStringExtension(base, ToolingExtensions.EXT_TYPE_PARAMETER); + if (bt == null) { + return true; + } else { + if (rule(errors, "2024-05-29", IssueType.INVALID, stack.getLiteralPath(), derived.hasExtension(ToolingExtensions.EXT_TYPE_PARAMETER), I18nConstants.SD_TYPE_PARAMETER_MISSING, base.getVersionedUrl(), bt, derived.getVersionedUrl())) { + String dt = ToolingExtensions.readStringExtension(derived, ToolingExtensions.EXT_TYPE_PARAMETER); + StructureDefinition bsd = context.fetchTypeDefinition(bt); + StructureDefinition dsd = context.fetchTypeDefinition(dt); + if (rule(errors, "2024-05-29", IssueType.INVALID, stack.getLiteralPath(), bsd != null, I18nConstants.SD_TYPE_PARAMETER_UNKNOWN, base.getVersionedUrl(), bt) && + rule(errors, "2024-05-29", IssueType.INVALID, stack.getLiteralPath(), dsd != null, I18nConstants.SD_TYPE_PARAMETER_UNKNOWN, derived.getVersionedUrl(), dt)) { + StructureDefinition t = dsd; + while (t != bsd && t != null) { + t = context.fetchResource(StructureDefinition.class, t.getBaseDefinition()); + } + return rule(errors, "2024-05-29", IssueType.INVALID, stack.getLiteralPath(), t != null, I18nConstants.SD_TYPE_PARAMETER_INVALID, base.getVersionedUrl(), bt, derived.getVersionedUrl(), dt); + } + } + return false; + } + } private String getFixedValue(Element src) { Element diff = src.getNamedChild("differential", false); @@ -465,7 +488,8 @@ public class StructureDefinitionValidator extends BaseValidator { typeCodes.add(tc); Set tcharacteristics = new HashSet<>(); StructureDefinition tsd = context.fetchTypeDefinition(tc); - if (tsd != null) { + if (tsd != null) { + checkTypeParameters(errors, stack, type, tc, tsd, path, sd); characteristicsValid = true; if (tsd.hasExtension(ToolingExtensions.EXT_TYPE_CHARACTERISTICS)) { for (Extension ext : tsd.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_CHARACTERISTICS)) { @@ -566,6 +590,54 @@ public class StructureDefinitionValidator extends BaseValidator { return ok; } + private boolean checkTypeParameters(List errors, NodeStack stack, Element typeE, String tc, StructureDefinition tsd, String path, StructureDefinition sd) { + boolean ok = true; + if (tsd.hasExtension(ToolingExtensions.EXT_TYPE_PARAMETER)) { + List extensions = typeE.getChildrenByName("extension"); + for (Extension ext : tsd.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_PARAMETER)) { + String name = ext.getExtensionString("name"); + String type = ext.getExtensionString("type"); + StructureDefinition psd = context.fetchTypeDefinition(type); + if (psd != null && name != null) { + boolean found = false; + for (Element e : extensions) { + if (ToolingExtensions.EXT_TYPE_PARAMETER.equals(e.getNamedChildValue("url"))) { + String ename = e.getExtensionValue("name").primitiveValue(); + if (name.equals(ename)) { + found = true; + String etype = e.getExtensionValue("type").primitiveValue(); + for (Extension ex : sd.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_PARAMETER)) { + String tn = ex.getExtensionString("name"); + if (tn != null && tn.equals(etype)) { + etype = ex.getExtensionString("type"); + break; + } + } + StructureDefinition esd = context.fetchTypeDefinition(etype); + if (rule(errors, "2024-05-29", IssueType.BUSINESSRULE, stack.getLiteralPath(), esd != null, I18nConstants.SD_TYPE_PARAMETER_UNKNOWN, tc, etype)) { + StructureDefinition t = esd; + while (t != null && t != psd) { + t = context.fetchResource(StructureDefinition.class, t.getBaseDefinition()); + } + ok = rule(errors, "2024-05-29", IssueType.BUSINESSRULE, stack.getLiteralPath(), t != null, I18nConstants.SD_TYPE_PARAMETER_INVALID_REF, tc, etype, tsd.getVersionedUrl(), name, type) & ok; + if (t != null) { + if (!sd.getAbstract() && esd.getAbstract()) { + warning(errors, "2024-05-29", IssueType.BUSINESSRULE, stack.getLiteralPath(), t != null, I18nConstants.SD_TYPE_PARAMETER_ABSTRACT_WARNING, tc, etype, tsd.getVersionedUrl(), name, type); + } + } + } else { + ok = false; + } + } + } + } + ok = rule(errors, "2024-05-29", IssueType.BUSINESSRULE, stack.getLiteralPath(), found, I18nConstants.SD_TYPE_PARAM_NOT_SPECIFIED, tc, tsd.getVersionedUrl(), name, path) && ok; + } + } + } + return ok; + } + private ElementDefinition getDefinitionFromBase(StructureDefinition base, String id, String path) { ElementDefinition ed = null; if (id != null) { 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 7b9fba170..f7bfaaff0 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 @@ -59,6 +59,7 @@ import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.r5.utils.OperationOutcomeUtilities; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.validation.BundleValidationRule; +import org.hl7.fhir.r5.utils.validation.IMessagingServices; import org.hl7.fhir.r5.utils.validation.IResourceValidator; import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor; import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher; @@ -90,6 +91,7 @@ import org.hl7.fhir.validation.ValidationEngine; import org.hl7.fhir.validation.ValidatorUtils; import org.hl7.fhir.validation.cli.model.HtmlInMarkdownCheck; import org.hl7.fhir.validation.cli.services.StandAloneValidatorFetcher; +import org.hl7.fhir.validation.instance.BasePolicyAdvisorForFullValidation; import org.hl7.fhir.validation.instance.InstanceValidator; import org.hl7.fhir.validation.tests.utilities.TestUtilities; import org.junit.AfterClass; @@ -469,7 +471,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe } private ValidationEngine buildVersionEngine(String ver, String txLog) throws Exception { - String server = FhirSettings.getTxFhirLocal(); + String server = FhirSettings.getTxFhirDevelopment(); switch (ver) { case "1.0": return TestUtilities.getValidationEngine("hl7.fhir.r2.core#1.0.2", server, txLog, FhirPublication.DSTU2, true, "1.0.2"); case "1.4": return TestUtilities.getValidationEngine("hl7.fhir.r2b.core#1.4.0", server, txLog, FhirPublication.DSTU2016May, true, "1.4.0"); @@ -887,4 +889,12 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe public Set fetchCanonicalResourceVersions(IResourceValidator validator, Object appContext, String url) { return new HashSet<>(); } + + @Override + public List getImpliedProfilesForResource(IResourceValidator validator, Object appContext, + String stackPath, ElementDefinition definition, StructureDefinition structure, Element resource, boolean valid, + IMessagingServices msgServices, List messages) { + return new BasePolicyAdvisorForFullValidation().getImpliedProfilesForResource(validator, appContext, stackPath, + definition, structure, resource, valid, msgServices, messages); + } } \ No newline at end of file diff --git a/pom.xml b/pom.xml index 06c61963f..05f3bbd3c 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ 1.26.0 32.0.1-jre 6.4.1 - 1.5.10 + 1.5.11-SNAPSHOT 2.17.0 5.9.2 1.8.2