Merge pull request #290 from hapifhir/gg-work-5018

various fixes for 5.0.18
This commit is contained in:
Grahame Grieve 2020-08-04 05:11:11 +10:00 committed by GitHub
commit 389ad1c180
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 1660 additions and 241 deletions

View File

@ -0,0 +1,16 @@
Validator Fixes:
* better validation of external references. Note: this is a potentially significant change: things that were called ok before may not be now, and things that were not ok before may become so, depending on the interplay between this and auto-load, further work may be needed here
* Support better validation of version specific profiles in meta.profile. This may also find new errors that were not previously being found
* Support auto-determination of the version of FHIR to use when using the java validator
* auto-load packages from the package server when references to profiles etc are encountered
* look for references inside other parameters in Parameters resource
* no validation for CanonicalResource.url (further work needed)
Other Code changes:
* Rendering: add rendering for Parameters resources
* Rendering: refactor of resource resolution code to support Parameters
* General clean up of rendering consistency & implement additional details when rendering (including patient summary)
* Rendering: major overhaul of DiagnosticReport rendering
* Fix NPE bug in value set comparison

View File

@ -154,7 +154,7 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
private void compareConcepts(List<ConceptDefinitionComponent> left, List<ConceptDefinitionComponent> right, StructuralMatch<ConceptDefinitionComponent> combined,
List<ConceptDefinitionComponent> union, List<ConceptDefinitionComponent> intersection, CodeSystem csU, CodeSystem csI, CodeSystemComparison res, String path) {
List<ConceptDefinitionComponent> union, List<ConceptDefinitionComponent> intersection, CodeSystem csU, CodeSystem csI, CodeSystemComparison res, String path) {
List<ConceptDefinitionComponent> matchR = new ArrayList<>();
for (ConceptDefinitionComponent l : left) {
ConceptDefinitionComponent r = findInList(right, l);

View File

@ -84,7 +84,12 @@ public class ComparisonRenderer implements IEvaluationContext {
first = false;
b.append("<tr><td colspan=4><b>"+name+"</b></td></tr>\r\n");
}
renderComparison(id, comp);
try {
renderComparison(id, comp);
} catch (Exception e) {
System.out.println("Exception rendering "+id+": "+e.getMessage());
e.printStackTrace();
}
b.append(comp.toTable());
//"<li><a href=\""+comp.getId()+".html\">"+Utilities.escapeXml(comp.summary())+"</a></li>\r\n"
}

View File

@ -210,7 +210,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
private int countMatchesBySystem(List<ConceptSetComponent> list, ConceptSetComponent item) {
int c = 0;
for (ConceptSetComponent t : list) {
if (t.getSystem().equals(item.getSystem())) {
if (t.hasSystem() && t.getSystem().equals(item.getSystem())) {
c++;
}
}
@ -545,8 +545,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
if (t.hasLeft() && t.hasRight()) {
ConceptSetComponent csL = (ConceptSetComponent) t.getLeft();
ConceptSetComponent csR = (ConceptSetComponent) t.getRight();
// we assume both have systems
if (csL.getSystem().equals(csR.getSystem())) {
if (csL.hasSystem() && csL.getSystem().equals(csR.getSystem())) {
r.getCells().add(gen.new Cell(null, null, csL.getSystem(), null, null).span(2).center());
} else {
r.getCells().add(gen.new Cell(null, null, csL.getSystem(), null, null).setStyle("background-color: "+COLOR_DIFFERENT));

View File

@ -267,6 +267,11 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
return res;
}
public static SimpleWorkerContext fromNothing() throws FileNotFoundException, FHIRException, IOException {
SimpleWorkerContext res = new SimpleWorkerContext();
return res;
}
private void loadDefinitionItem(String name, InputStream stream, IContextResourceLoader loader, ILoadFilter filter, PackageVersion pi) throws IOException, FHIRException {
if (name.endsWith(".xml"))
loadFromFile(stream, name, loader, filter);

View File

@ -37,8 +37,8 @@ public class BundleRenderer extends ResourceRenderer {
}
@Override
public boolean render(XhtmlNode x, DomainResource r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
return false;
public boolean render(XhtmlNode x, Resource r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
return render(x, (Bundle) r);
}
@Override
@ -46,6 +46,11 @@ public class BundleRenderer extends ResourceRenderer {
return null;
}
@Override
public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
return null;
}
@Override
public boolean render(XhtmlNode x, ResourceWrapper b) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
List<BaseWrapper> entries = b.children("entry");

View File

@ -15,6 +15,7 @@ import org.hl7.fhir.r5.model.CapabilityStatement.SystemInteractionComponent;
import org.hl7.fhir.r5.model.CapabilityStatement.SystemRestfulInteraction;
import org.hl7.fhir.r5.model.CapabilityStatement.TypeRestfulInteraction;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
import org.hl7.fhir.utilities.xhtml.NodeType;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
@ -29,7 +30,7 @@ public class CapabilityStatementRenderer extends ResourceRenderer {
super(context, rcontext);
}
public boolean render(XhtmlNode x, DomainResource dr) throws FHIRFormatError, DefinitionException, IOException {
public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException {
return render(x, (CapabilityStatement) dr);
}
@ -149,4 +150,14 @@ public class CapabilityStatementRenderer extends ResourceRenderer {
tr.td().addText(value);
}
public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
if (r.has("title")) {
return r.children("title").get(0).getBase().primitiveValue();
}
if (r.has("name")) {
return r.children("name").get(0).getBase().primitiveValue();
}
return "??";
}
}

View File

@ -19,6 +19,7 @@ import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Enumeration;
import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
@ -38,7 +39,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
}
public boolean render(XhtmlNode x, DomainResource dr) throws FHIRFormatError, DefinitionException, IOException {
public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException {
return render(x, (CodeSystem) dr);
}

View File

@ -11,6 +11,7 @@ import org.hl7.fhir.r5.model.StringType;
import org.hl7.fhir.r5.model.CompartmentDefinition;
import org.hl7.fhir.r5.model.CompartmentDefinition.CompartmentDefinitionResourceComponent;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
@ -26,7 +27,7 @@ public class CompartmentDefinitionRenderer extends ResourceRenderer {
super(context, rcontext);
}
public boolean render(XhtmlNode x, DomainResource dr) throws FHIRFormatError, DefinitionException, IOException {
public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException {
return render(x, (CompartmentDefinition) dr);
}
@ -72,4 +73,14 @@ public class CompartmentDefinitionRenderer extends ResourceRenderer {
return ((CompartmentDefinition) r).present();
}
public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
if (r.has("title")) {
return r.children("title").get(0).getBase().primitiveValue();
}
if (r.has("name")) {
return r.children("name").get(0).getBase().primitiveValue();
}
return "??";
}
}

View File

@ -18,6 +18,7 @@ import org.hl7.fhir.r5.model.ContactDetail;
import org.hl7.fhir.r5.model.ContactPoint;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Enumerations.ConceptMapRelationship;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
import org.hl7.fhir.r5.utils.ToolingExtensions;
@ -34,7 +35,7 @@ public class ConceptMapRenderer extends TerminologyRenderer {
super(context, rcontext);
}
public boolean render(XhtmlNode x, DomainResource dr) throws FHIRFormatError, DefinitionException, IOException {
public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException {
return render(x, (ConceptMap) dr);
}

View File

@ -1,6 +1,7 @@
package org.hl7.fhir.r5.renderers;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import org.hl7.fhir.exceptions.DefinitionException;
@ -11,6 +12,7 @@ import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult;
import org.hl7.fhir.r5.model.Address;
import org.hl7.fhir.r5.model.Annotation;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.BaseDateTimeType;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.CodeableConcept;
@ -23,6 +25,7 @@ import org.hl7.fhir.r5.model.Enumeration;
import org.hl7.fhir.r5.model.HumanName;
import org.hl7.fhir.r5.model.HumanName.NameUse;
import org.hl7.fhir.r5.model.Identifier;
import org.hl7.fhir.r5.model.InstantType;
import org.hl7.fhir.r5.model.Period;
import org.hl7.fhir.r5.model.PrimitiveType;
import org.hl7.fhir.r5.model.Quantity;
@ -50,7 +53,7 @@ import org.hl7.fhir.utilities.validation.ValidationOptions;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.hl7.fhir.utilities.xhtml.XhtmlParser;
public class DataRenderer {
public class DataRenderer extends Renderer {
// -- 1. context --------------------------------------------------------------
@ -193,7 +196,6 @@ public class DataRenderer {
return lang;
}
private boolean isCanonical(String path) {
if (!path.endsWith(".url"))
return false;
@ -269,8 +271,19 @@ public class DataRenderer {
return "to do";
}
public void render(XhtmlNode x, BaseWrapper type) {
x.tx("to do");
public void render(XhtmlNode x, BaseWrapper type) {
Base base = null;
try {
base = type.getBase();
} catch (FHIRException | IOException e) {
x.tx("Error: " + e.getMessage()); // this shouldn't happen - it's an error in the library itself
return;
}
if (base instanceof DataType) {
render(x, (DataType) base);
} else {
x.tx("to do: "+base.fhirType());
}
}
public void renderBase(XhtmlNode x, Base b) {
@ -312,6 +325,10 @@ public class DataRenderer {
renderSampledData(x, (SampledData) type);
} else if (type instanceof Reference) {
renderReference(x, (Reference) type);
} else if (type instanceof InstantType) {
x.tx(((InstantType) type).toHumanDisplay());
} else if (type instanceof BaseDateTimeType) {
x.tx(((BaseDateTimeType) type).toHumanDisplay());
} else if (type.isPrimitive()) {
x.tx(type.primitiveValue());
} else {
@ -367,31 +384,37 @@ public class DataRenderer {
}
protected void renderAnnotation(XhtmlNode x, Annotation a, boolean showCodeDetails) throws FHIRException {
StringBuilder s = new StringBuilder();
if (a.hasAuthor()) {
s.append("Author: ");
StringBuilder b = new StringBuilder();
if (a.hasText()) {
b.append(a.getText());
}
if (a.hasAuthorReference())
s.append(a.getAuthorReference().getReference());
else if (a.hasAuthorStringType())
s.append(a.getAuthorStringType().getValue());
if (a.hasText() && (a.hasAuthor() || a.hasTimeElement())) {
b.append(" (");
}
if (a.hasAuthor()) {
b.append("By ");
if (a.hasAuthorReference()) {
b.append(a.getAuthorReference().getReference());
} else if (a.hasAuthorStringType()) {
b.append(a.getAuthorStringType().getValue());
}
}
if (a.hasTimeElement()) {
if (s.length() > 0)
s.append("; ");
s.append("Made: ").append(a.getTimeElement().toHumanDisplay());
if (b.length() > 0) {
b.append(" ");
}
b.append("@").append(a.getTimeElement().toHumanDisplay());
}
if (a.hasText() && (a.hasAuthor() || a.hasTimeElement())) {
b.append(")");
}
if (a.hasText()) {
if (s.length() > 0)
s.append("; ");
s.append("Annotation: ").append(a.getText());
}
x.addText(s.toString());
x.addText(b.toString());
}
public String displayCoding(Coding c) {
@ -563,11 +586,13 @@ public class DataRenderer {
if (ii.hasType()) {
if (ii.getType().hasText())
s = ii.getType().getText()+" = "+s;
s = ii.getType().getText()+": "+s;
else if (ii.getType().hasCoding() && ii.getType().getCoding().get(0).hasDisplay())
s = ii.getType().getCoding().get(0).getDisplay()+" = "+s;
s = ii.getType().getCoding().get(0).getDisplay()+": "+s;
else if (ii.getType().hasCoding() && ii.getType().getCoding().get(0).hasCode())
s = lookupCode(ii.getType().getCoding().get(0).getSystem(), ii.getType().getCoding().get(0).getCode())+" = "+s;
s = lookupCode(ii.getType().getCoding().get(0).getSystem(), ii.getType().getCoding().get(0).getCode())+": "+s;
} else {
s = "id: "+s;
}
if (ii.hasUse())

View File

@ -10,6 +10,8 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.DataType;
import org.hl7.fhir.r5.model.DiagnosticReport;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.PropertyWrapper;
@ -18,6 +20,7 @@ import org.hl7.fhir.r5.renderers.utils.DirectWrappers;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceWithReference;
import org.hl7.fhir.r5.utils.EOperationOutcome;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
@ -38,11 +41,11 @@ public class DiagnosticReportRenderer extends ResourceRenderer {
super(context, rcontext);
}
public boolean render(XhtmlNode x, DomainResource dr) throws FHIRFormatError, DefinitionException, IOException {
public boolean render(XhtmlNode x, Resource dr) throws IOException, FHIRException, EOperationOutcome {
return render(x, (DiagnosticReport) dr);
}
public boolean render(XhtmlNode x, ResourceWrapper dr) throws FHIRFormatError, DefinitionException, IOException {
public boolean render(XhtmlNode x, ResourceWrapper dr) throws IOException, FHIRException, EOperationOutcome {
XhtmlNode h2 = x.h2();
render(h2, getProperty(dr, "code").value());
h2.tx(" ");
@ -56,40 +59,55 @@ public class DiagnosticReportRenderer extends ResourceRenderer {
}
h2.tx(") ");
}
if (dr.has("issued")) {
render(h2, getProperty(dr, "issued").value());
XhtmlNode tbl = x.table("grid");
XhtmlNode tr;
if (dr.has("subject")) {
tr = tbl.tr();
tr.td().tx("Subject");
populateSubjectSummary(tr.td(), getProperty(dr, "subject").value());
}
XhtmlNode tbl = x.table("grid");
XhtmlNode tr = tbl.tr();
XhtmlNode tdl = tr.td();
XhtmlNode tdr = tr.td();
if (dr.has("subject")) {
populateSubjectSummary(tdl, getProperty(dr, "subject").value());
DataType eff = null;
DataType iss = null;
if (dr.has("effective[x]")) {
tr = tbl.tr();
tr.td().tx("When For");
eff = (DataType) getProperty(dr, "effective[x]").value().getBase();
render(tr.td(), eff);
}
tdr.b().tx("Report Details");
tdr.br();
if (dr.has("issued")) {
tr = tbl.tr();
tr.td().tx("Reported");
eff = (DataType) getProperty(dr, "effective[x]").value().getBase();
render(tr.td(), getProperty(dr, "issued").value());
}
pw = getProperty(dr, "perfomer");
if (valued(pw)) {
tdr.addText(Utilities.pluralize("Performer", pw.getValues().size())+":");
tr = tbl.tr();
tr.td().tx(Utilities.pluralize("Performer", pw.getValues().size()));
XhtmlNode tdr = tr.td();
for (BaseWrapper v : pw.getValues()) {
tdr.tx(" ");
render(tdr, v);
}
tdr.br();
}
pw = getProperty(dr, "identifier");
if (valued(pw)) {
tdr.addText(Utilities.pluralize("Identifier", pw.getValues().size())+":");
tr = tbl.tr();
tr.td().tx(Utilities.pluralize("Identifier", pw.getValues().size())+":");
XhtmlNode tdr = tr.td();
for (BaseWrapper v : pw.getValues()) {
tdr.tx(" ");
render(tdr, v);
}
tdr.br();
}
pw = getProperty(dr, "request");
if (valued(pw)) {
tdr.addText(Utilities.pluralize("Request", pw.getValues().size())+":");
tr = tbl.tr();
tr.td().tx(Utilities.pluralize("Request", pw.getValues().size())+":");
XhtmlNode tdr = tr.td();
for (BaseWrapper v : pw.getValues()) {
tdr.tx(" ");
render(tdr, v);
@ -97,29 +115,35 @@ public class DiagnosticReportRenderer extends ResourceRenderer {
tdr.br();
}
x.para().b().tx("Report Details");
pw = getProperty(dr, "result");
if (valued(pw)) {
List<ObservationNode> observations = fetchObservations(pw.getValues(), dr);
buildObservationsTable(x, observations);
buildObservationsTable(x, observations, eff, iss);
}
pw = getProperty(dr, "conclusion");
if (valued(pw))
render(x.para(), pw.value());
pw = getProperty(dr, "result");
pw = getProperty(dr, "conclusionCode");
if (!valued(pw)) {
pw = getProperty(dr, "codedDiagnosis");
}
if (valued(pw)) {
XhtmlNode p = x.para();
p.b().tx("Coded Diagnoses :");
p.b().tx("Coded Conclusions :");
XhtmlNode ul = x.ul();
for (BaseWrapper v : pw.getValues()) {
tdr.tx(" ");
render(tdr, v);
render(ul.li(), v);
}
}
return false;
}
public boolean render(XhtmlNode x, DiagnosticReport dr) throws FHIRFormatError, DefinitionException, IOException {
public boolean render(XhtmlNode x, DiagnosticReport dr) throws IOException, FHIRException, EOperationOutcome {
render(x, new DirectWrappers.ResourceWrapperDirect(this.context, dr));
return true;
@ -138,7 +162,12 @@ public class DiagnosticReportRenderer extends ResourceRenderer {
return display((DiagnosticReport) r);
}
private void populateSubjectSummary(XhtmlNode container, BaseWrapper subject) throws UnsupportedEncodingException, FHIRException, IOException {
@Override
public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
return "Not done yet";
}
private void populateSubjectSummary(XhtmlNode container, BaseWrapper subject) throws UnsupportedEncodingException, FHIRException, IOException, EOperationOutcome {
ResourceWrapper r = fetchResource(subject);
if (r == null)
container.tx("Unable to get Patient Details");
@ -148,8 +177,8 @@ public class DiagnosticReportRenderer extends ResourceRenderer {
container.tx("Not done yet");
}
private void generatePatientSummary(XhtmlNode c, ResourceWrapper r) {
c.tx("to do");
private void generatePatientSummary(XhtmlNode c, ResourceWrapper r) throws FHIRFormatError, DefinitionException, FHIRException, IOException, EOperationOutcome {
new PatientRenderer(context).describe(c, r);
}
private List<ObservationNode> fetchObservations(List<BaseWrapper> list, ResourceWrapper rw) throws UnsupportedEncodingException, FHIRException, IOException {
@ -171,40 +200,165 @@ public class DiagnosticReportRenderer extends ResourceRenderer {
return res;
}
private void buildObservationsTable(XhtmlNode root, List<ObservationNode> observations) {
XhtmlNode tbl = root.table( "none");
private void buildObservationsTable(XhtmlNode root, List<ObservationNode> observations, DataType eff, DataType iss) throws UnsupportedEncodingException, FHIRException, IOException {
XhtmlNode tbl = root.table("grid");
boolean refRange = scanObsForRefRange(observations);
boolean flags = scanObsForFlags(observations);
boolean note = scanObsForNote(observations);
boolean effectiveTime = scanObsForEffective(observations, eff);
boolean issued = scanObsForIssued(observations, iss);
int cs = 2;
if (refRange) cs++;
if (flags) cs++;
if (note) cs++;
if (issued) cs++;
if (effectiveTime) cs++;
XhtmlNode tr = tbl.tr();
tr.td().b().tx("Code");
tr.td().b().tx("Value");
if (refRange) {
tr.td().b().tx("Reference Range");
}
if (flags) {
tr.td().b().tx("Flags");
}
if (note) {
tr.td().b().tx("Note");
}
if (effectiveTime) {
tr.td().b().tx("When For");
}
if (issued) {
tr.td().b().tx("Reported");
}
for (ObservationNode o : observations) {
addObservationToTable(tbl, o, 0);
addObservationToTable(tbl, o, 0, Integer.toString(cs), refRange, flags, note, effectiveTime, issued, eff, iss);
}
}
private void addObservationToTable(XhtmlNode tbl, ObservationNode o, int i) {
private boolean scanObsForRefRange(List<ObservationNode> observations) {
for (ObservationNode o : observations) {
if (o.obs.getResource() != null) {
PropertyWrapper pw = getProperty(o.obs.getResource(), "referenceRange");
if (valued(pw)) {
return true;
}
}
if (o.contained != null) {
if (scanObsForRefRange(o.contained)) {
return true;
}
}
}
return false;
}
private boolean scanObsForNote(List<ObservationNode> observations) {
for (ObservationNode o : observations) {
if (o.obs.getResource() != null) {
PropertyWrapper pw = getProperty(o.obs.getResource(), "note");
if (valued(pw)) {
return true;
}
}
if (o.contained != null) {
if (scanObsForNote(o.contained)) {
return true;
}
}
}
return false;
}
private boolean scanObsForIssued(List<ObservationNode> observations, DataType iss) throws UnsupportedEncodingException, FHIRException, IOException {
for (ObservationNode o : observations) {
if (o.obs.getResource() != null) {
PropertyWrapper pw = getProperty(o.obs.getResource(), "issued");
if (valued(pw)) {
if (!Base.compareDeep(pw.value().getBase(), iss, true)) {
return true;
}
}
}
if (o.contained != null) {
if (scanObsForIssued(o.contained, iss)) {
return true;
}
}
}
return false;
}
private boolean scanObsForEffective(List<ObservationNode> observations, DataType eff) throws UnsupportedEncodingException, FHIRException, IOException {
for (ObservationNode o : observations) {
if (o.obs.getResource() != null) {
PropertyWrapper pw = getProperty(o.obs.getResource(), "effective[x]");
if (valued(pw)) {
if (!Base.compareDeep(pw.value().getBase(), eff, true)) {
return true;
}
}
}
if (o.contained != null) {
if (scanObsForEffective(o.contained, eff)) {
return true;
}
}
}
return false;
}
private boolean scanObsForFlags(List<ObservationNode> observations) throws UnsupportedEncodingException, FHIRException, IOException {
for (ObservationNode o : observations) {
if (o.obs.getResource() != null) {
PropertyWrapper pw = getProperty(o.obs.getResource(), "interpretation");
if (valued(pw)) {
return true;
}
pw = getProperty(o.obs.getResource(), "status");
if (valued(pw)) {
if (!pw.value().getBase().primitiveValue().equals("final")) {
return true;
}
}
}
if (o.contained != null) {
if (scanObsForFlags(o.contained)) {
return true;
}
}
}
return false;
}
private void addObservationToTable(XhtmlNode tbl, ObservationNode o, int i, String cs, boolean refRange, boolean flags, boolean note, boolean effectiveTime, boolean issued, DataType eff, DataType iss) throws UnsupportedEncodingException, FHIRException, IOException {
XhtmlNode tr = tbl.tr();
if (o.obs.getReference() == null) {
XhtmlNode td = tr.td().colspan("6");
XhtmlNode td = tr.td().colspan(cs);
td.i().tx("This Observation could not be resolved");
} else {
if (o.obs.getResource() != null) {
addObservationToTable(tr, o.obs.getResource(), i);
addObservationToTable(tr, o.obs.getResource(), i, o.obs.getReference(), refRange, flags, note, effectiveTime, issued, eff, iss);
} else {
tr.td().tx("Unable to resolve Observation: "+o.obs.getReference());
XhtmlNode td = tr.td().colspan(cs);
td.i().ah(o.obs.getReference()).tx("Observation");
}
if (o.contained != null) {
for (ObservationNode c : o.contained) {
addObservationToTable(tbl, c, i+1);
addObservationToTable(tbl, c, i+1, cs, refRange, flags, note, effectiveTime, issued, eff, iss);
}
}
}
}
private void addObservationToTable(XhtmlNode tr, ResourceWrapper obs, int i) {
// TODO Auto-generated method stub
private void addObservationToTable(XhtmlNode tr, ResourceWrapper obs, int i, String ref, boolean refRange, boolean flags, boolean note, boolean effectiveTime, boolean issued, DataType eff, DataType iss) throws UnsupportedEncodingException, FHIRException, IOException {
// code (+bodysite)
XhtmlNode td = tr.td();
PropertyWrapper pw = getProperty(obs, "code");
if (valued(pw)) {
render(td, pw.value());
render(td.ah(ref), pw.value());
}
pw = getProperty(obs, "bodySite");
if (valued(pw)) {
@ -218,23 +372,113 @@ public class DiagnosticReportRenderer extends ResourceRenderer {
pw = getProperty(obs, "value[x]");
if (valued(pw)) {
render(td, pw.value());
} else {
pw = getProperty(obs, "dataAbsentReason");
if (valued(pw)) {
XhtmlNode span = td.span("color: maroon", "Error");
span.tx("Error: ");
render(span.b(), pw.value());
}
}
if (refRange) {
// reference range
td = tr.td();
pw = getProperty(obs, "referenceRange");
if (valued(pw)) {
boolean first = true;
for (BaseWrapper v : pw.getValues()) {
if (first) first = false; else td.br();
PropertyWrapper pwr = getProperty(v, "type");
if (valued(pwr)) {
render(td, pwr.value());
td.tx(": ");
}
PropertyWrapper pwt = getProperty(v, "text");
if (valued(pwt)) {
render(td, pwt.value());
} else {
PropertyWrapper pwl = getProperty(v, "low");
PropertyWrapper pwh = getProperty(v, "high");
if (valued(pwl) && valued(pwh)) {
render(td, pwl.value());
td.tx(" - ");
render(td, pwh.value());
} else if (valued(pwl)) {
td.tx(">");
render(td, pwl.value());
} else if (valued(pwh)) {
td.tx("<");
render(td, pwh.value());
} else {
td.tx("??");
}
}
pwr = getProperty(v, "appliesTo");
PropertyWrapper pwrA = getProperty(v, "age");
if (valued(pwr) || valued(pwrA)) {
boolean firstA = true;
td.tx(" for ");
if (valued(pwr)) {
for (BaseWrapper va : pwr.getValues()) {
if (firstA) firstA = false; else td.tx(", ");
render(td, va);
}
}
if (valued(pwrA)) {
if (firstA) firstA = false; else td.tx(", ");
td.tx("Age ");
render(td, pwrA.value());
}
}
}
}
}
if (flags) {
// flags (status other than F, interpretation, )
td = tr.td();
boolean first = true;
pw = getProperty(obs, "status");
if (valued(pw)) {
if (!pw.value().getBase().primitiveValue().equals("final")) {
if (first) first = false; else td.br();
render(td, pw.value());
}
}
pw = getProperty(obs, "interpretation");
if (valued(pw)) {
for (BaseWrapper v : pw.getValues()) {
if (first) first = false; else td.br();
render(td, v);
}
}
}
// units
td = tr.td();
td.tx("to do");
// reference range
td = tr.td();
td.tx("to do");
// flags (status other than F, interpretation, )
td = tr.td();
td.tx("to do");
// issued if different to DR
td = tr.td();
td.tx("to do");
if (note) {
td = tr.td();
pw = getProperty(obs, "note");
if (valued(pw)) {
render(td, pw.value());
}
}
if (effectiveTime) {
// effective if different to DR
td = tr.td();
pw = getProperty(obs, "effective[x]");
if (valued(pw)) {
if (!Base.compareDeep(pw.value().getBase(), eff, true)) {
render(td, pw.value());
}
}
}
if (issued) {
// issued if different to DR
td = tr.td();
pw = getProperty(obs, "issued");
if (valued(pw)) {
if (!Base.compareDeep(pw.value().getBase(), eff, true)) {
render(td, pw.value());
}
}
}
}
}

View File

@ -6,6 +6,7 @@ import java.io.UnsupportedEncodingException;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
public class EncounterRenderer extends ResourceRenderer {
@ -15,7 +16,7 @@ public class EncounterRenderer extends ResourceRenderer {
super(context);
}
public boolean render(XhtmlNode x, DomainResource dr) throws UnsupportedEncodingException, IOException {
public boolean render(XhtmlNode x, Resource dr) throws UnsupportedEncodingException, IOException {
describe(x, dr);
return false;
}
@ -24,4 +25,10 @@ public class EncounterRenderer extends ResourceRenderer {
return "Not done yet";
}
@Override
public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
return "Not done yet";
}
}

View File

@ -9,6 +9,7 @@ import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.ImplementationGuide;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
@ -22,7 +23,7 @@ public class ImplementationGuideRenderer extends ResourceRenderer {
super(context, rcontext);
}
public boolean render(XhtmlNode x, DomainResource dr) throws FHIRFormatError, DefinitionException, IOException {
public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException {
return render(x, (ImplementationGuide) dr);
}
@ -47,4 +48,14 @@ public class ImplementationGuideRenderer extends ResourceRenderer {
return ((ImplementationGuide) r).present();
}
public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
if (r.has("title")) {
return r.children("title").get(0).getBase().primitiveValue();
}
if (r.has("name")) {
return r.children("name").get(0).getBase().primitiveValue();
}
return "??";
}
}

View File

@ -34,7 +34,7 @@ public class LiquidRenderer extends ResourceRenderer {
}
@Override
public boolean render(XhtmlNode x, DomainResource r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
public boolean render(XhtmlNode x, Resource r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
LiquidEngine engine = new LiquidEngine(context.getWorker(), context.getServices());
XhtmlNode xn;
try {
@ -56,6 +56,16 @@ public class LiquidRenderer extends ResourceRenderer {
return "not done yet";
}
public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
if (r.has("title")) {
return r.children("title").get(0).getBase().primitiveValue();
}
if (r.has("name")) {
return r.children("name").get(0).getBase().primitiveValue();
}
return "??";
}
@Override
public boolean render(XhtmlNode x, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
LiquidEngine engine = new LiquidEngine(context.getWorker(), context.getServices());

View File

@ -29,7 +29,7 @@ public class ListRenderer extends ResourceRenderer {
super(context, rcontext);
}
public boolean render(XhtmlNode x, DomainResource dr) throws FHIRFormatError, DefinitionException, IOException {
public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException {
return render(x, (ListResource) dr);
}
@ -189,6 +189,14 @@ public class ListRenderer extends ResourceRenderer {
return ((ListResource) r).getTitle();
}
@Override
public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
if (r.has("title")) {
return r.children("title").get(0).getBase().primitiveValue();
}
return "??";
}
private void shortForRef(XhtmlNode x, Reference ref) throws UnsupportedEncodingException, IOException {
ResourceWithReference r = context.getResolver().resolve(context, ref.getReference());
if (r == null) {

View File

@ -12,6 +12,7 @@ import org.hl7.fhir.r5.model.PrimitiveType;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.utils.ToolingExtensions;
@ -28,7 +29,7 @@ public class NamingSystemRenderer extends ResourceRenderer {
super(context, rcontext);
}
public boolean render(XhtmlNode x, DomainResource dr) throws FHIRFormatError, DefinitionException, IOException {
public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException {
return render(x, (NamingSystem) dr);
}
@ -125,4 +126,14 @@ public class NamingSystemRenderer extends ResourceRenderer {
return ((NamingSystem) r).present();
}
public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
if (r.has("title")) {
return r.children("title").get(0).getBase().primitiveValue();
}
if (r.has("name")) {
return r.children("name").get(0).getBase().primitiveValue();
}
return "??";
}
}

View File

@ -30,7 +30,7 @@ public class OperationDefinitionRenderer extends TerminologyRenderer {
super(context, rcontext);
}
public boolean render(XhtmlNode x, DomainResource dr) throws IOException, FHIRException, EOperationOutcome {
public boolean render(XhtmlNode x, Resource dr) throws IOException, FHIRException, EOperationOutcome {
return render(x, (OperationDefinition) dr);
}

View File

@ -14,6 +14,7 @@ import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StringType;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
@ -29,7 +30,7 @@ public class OperationOutcomeRenderer extends ResourceRenderer {
super(context, rcontext);
}
public boolean render(XhtmlNode x, DomainResource dr) throws FHIRFormatError, DefinitionException, IOException {
public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException {
return render(x, (OperationOutcome) dr);
}
@ -85,6 +86,11 @@ public class OperationOutcomeRenderer extends ResourceRenderer {
return "todo";
}
@Override
public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
return "Not done yet";
}
@Override
public String display(Resource r) throws UnsupportedEncodingException, IOException {
return display((OperationOutcome) r);

View File

@ -0,0 +1,124 @@
package org.hl7.fhir.r5.renderers;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r5.model.Bundle.BundleEntryRequestComponent;
import org.hl7.fhir.r5.model.Bundle.BundleEntryResponseComponent;
import org.hl7.fhir.r5.model.Bundle.BundleEntrySearchComponent;
import org.hl7.fhir.r5.model.Bundle.BundleType;
import org.hl7.fhir.r5.model.Composition;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r5.model.Provenance;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.PropertyWrapper;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
import org.hl7.fhir.r5.utils.EOperationOutcome;
import org.hl7.fhir.utilities.xhtml.NodeType;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
public class ParametersRenderer extends ResourceRenderer {
public ParametersRenderer(RenderingContext context, ResourceContext rcontext) {
super(context, rcontext);
}
@Override
public boolean render(XhtmlNode x, Resource r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
return false;
}
@Override
public String display(Resource r) throws UnsupportedEncodingException, IOException {
return null;
}
@Override
public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
return null;
}
@Override
public boolean render(XhtmlNode x, ResourceWrapper params) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
x.h2().tx("Parameters");
XhtmlNode tbl = x.table("grid");
PropertyWrapper pw = getProperty(params, "parameter");
if (valued(pw)) {
paramsW(tbl, pw.getValues(), 0);
}
return false;
}
private void paramsW(XhtmlNode tbl, List<BaseWrapper> list, int indent) throws FHIRFormatError, DefinitionException, FHIRException, IOException, EOperationOutcome {
for (BaseWrapper p : list) {
XhtmlNode tr = tbl.tr();
XhtmlNode td = tr.td();
for (int i = 0; i < indent; i++) {
td.tx(XhtmlNode.NBSP);
}
td.tx(p.get("name").primitiveValue());
if (p.has("value")) {
renderBase(tr.td(), p.get("value"));
} else if (p.has("resource")) {
ResourceWrapper rw = p.getChildByName("resource").getAsResource();
td = tr.td();
XhtmlNode para = td.para();
para.tx(rw.fhirType()+"/"+rw.getId());
para.an(rw.fhirType().toLowerCase()+"_"+rw.getId()).tx(" ");
XhtmlNode x = rw.getNarrative();
if (x != null) {
td.addChildren(x);
} else {
ResourceRenderer rr = RendererFactory.factory(rw, context, rcontext);
rr.render(td, rw);
}
} else if (p.has("part")) {
tr.td();
PropertyWrapper pw = getProperty(p, "parameter");
paramsW(tbl, pw.getValues(), 1);
}
}
}
public XhtmlNode render(Parameters params) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
XhtmlNode div = new XhtmlNode(NodeType.Element, "div");
div.h2().tx("Parameters");
XhtmlNode tbl = div.table("grid");
params(tbl, params.getParameter(), 0);
return div;
}
private void params(XhtmlNode tbl, List<ParametersParameterComponent> list, int indent) throws FHIRFormatError, DefinitionException, FHIRException, IOException, EOperationOutcome {
for (ParametersParameterComponent p : list) {
XhtmlNode tr = tbl.tr();
XhtmlNode td = tr.td();
for (int i = 0; i < indent; i++) {
td.tx(XhtmlNode.NBSP);
}
td.tx(p.getName());
if (p.hasValue()) {
render(tr.td(), p.getValue());
} else if (p.hasResource()) {
ResourceRenderer rr = RendererFactory.factory(p.getResource(), context);
rr.render(tr.td(), p.getResource());
} else if (p.hasPart()) {
tr.td();
params(tbl, p.getPart(), 1);
}
}
}
}

View File

@ -3,9 +3,20 @@ package org.hl7.fhir.r5.renderers;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import org.apache.poi.hssf.record.chart.DatRecord;
import org.hl7.fhir.r5.model.DateTimeType;
import org.hl7.fhir.r5.model.DateType;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.HumanName;
import org.hl7.fhir.r5.model.HumanName.NameUse;
import org.hl7.fhir.r5.model.Identifier;
import org.hl7.fhir.r5.model.Identifier.IdentifierUse;
import org.hl7.fhir.r5.model.Patient;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.PropertyWrapper;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
public class PatientRenderer extends ResourceRenderer {
@ -16,13 +27,179 @@ public class PatientRenderer extends ResourceRenderer {
super(context);
}
public boolean render(XhtmlNode x, DomainResource dr) throws UnsupportedEncodingException, IOException {
public boolean render(XhtmlNode x, Resource dr) throws UnsupportedEncodingException, IOException {
describe(x, dr);
return false;
}
// name gender DoB (MRN)
public String display(Resource dr) {
return "Not done yet";
Patient pat = (Patient) dr;
Identifier id = null;
for (Identifier t : pat.getIdentifier()) {
id = chooseId(id, t);
}
HumanName n = null;
for (HumanName t : pat.getName()) {
n = chooseName(n, t);
}
return display(n, pat.getGender().getDisplay(), pat.getBirthDateElement(), id);
}
private Identifier chooseId(Identifier oldId, Identifier newId) {
if (oldId == null) {
return newId;
}
if (newId == null) {
return oldId;
}
return isPreferred(newId.getUse(), oldId.getUse()) ? newId : oldId;
}
private boolean isPreferred(IdentifierUse newUse, IdentifierUse oldUse) {
if (newUse == null && oldUse == null || newUse == oldUse) {
return false;
}
if (newUse == null) {
return true;
}
switch (newUse) {
case NULL: return !existsInList(oldUse, IdentifierUse.OFFICIAL, IdentifierUse.USUAL);
case OFFICIAL: return !existsInList(oldUse, IdentifierUse.USUAL);
case OLD: return !existsInList(oldUse, IdentifierUse.OFFICIAL, IdentifierUse.SECONDARY, IdentifierUse.USUAL);
case SECONDARY: return !existsInList(oldUse, IdentifierUse.OFFICIAL, IdentifierUse.USUAL);
case TEMP: return !existsInList(oldUse, IdentifierUse.OFFICIAL, IdentifierUse.SECONDARY, IdentifierUse.USUAL);
case USUAL: return true;
default: return false;
}
}
private boolean existsInList(IdentifierUse oldUse, IdentifierUse... values) {
for (IdentifierUse value : values) {
if (value == oldUse) {
return true;
}
}
return false;
}
private HumanName chooseName(HumanName oldName, HumanName newName) {
if (oldName == null) {
return newName;
}
if (newName == null) {
return oldName;
}
return isPreferred(newName.getUse(), oldName.getUse()) ? newName : oldName;
}
private boolean isPreferred(NameUse newUse, NameUse oldUse) {
if (newUse == null && oldUse == null || newUse == oldUse) {
return false;
}
if (newUse == null) {
return true;
}
switch (oldUse) {
case ANONYMOUS: return existsInList(newUse, NameUse.OFFICIAL, NameUse.USUAL);
case MAIDEN: return existsInList(newUse, NameUse.OFFICIAL, NameUse.USUAL);
case NICKNAME: return existsInList(newUse, NameUse.OFFICIAL, NameUse.USUAL);
case NULL: return existsInList(newUse, NameUse.OFFICIAL, NameUse.USUAL);
case OFFICIAL: return existsInList(newUse, NameUse.USUAL);
case OLD: return existsInList(newUse, NameUse.OFFICIAL, NameUse.USUAL);
case TEMP: return existsInList(newUse, NameUse.OFFICIAL, NameUse.USUAL);
case USUAL: return false;
}
return false;
}
private boolean existsInList(NameUse oldUse, NameUse... values) {
for (NameUse value : values) {
if (value == oldUse) {
return true;
}
}
return false;
}
@Override
public String display(ResourceWrapper pat) throws UnsupportedEncodingException, IOException {
Identifier id = null;
PropertyWrapper pw = getProperty(pat, "identifier");
for (BaseWrapper t : pw.getValues()) {
id = chooseId(id, (Identifier) t.getBase());
}
pw = getProperty(pat, "name");
HumanName n = null;
for (BaseWrapper t : pw.getValues()) {
n = chooseName(n, (HumanName) t);
}
String gender = null;
pw = getProperty(pat, "gender");
if (valued(pw)) {
pw.value().getBase().primitiveValue();
}
DateType dt = null;
pw = getProperty(pat, "birthDate");
if (valued(pw)) {
dt = (DateType) pw.value().getBase();
}
return display(n, gender, dt, id);
}
public void describe(XhtmlNode x, ResourceWrapper pat) throws UnsupportedEncodingException, IOException {
Identifier id = null;
PropertyWrapper pw = getProperty(pat, "identifier");
for (BaseWrapper t : pw.getValues()) {
id = chooseId(id, (Identifier) t.getBase());
}
pw = getProperty(pat, "name");
HumanName n = null;
for (BaseWrapper t : pw.getValues()) {
n = chooseName(n, (HumanName) t.getBase());
}
String gender = null;
pw = getProperty(pat, "gender");
if (valued(pw)) {
pw.value().getBase().primitiveValue();
}
DateType dt = null;
pw = getProperty(pat, "birthDate");
if (valued(pw)) {
dt = (DateType) pw.value().getBase();
}
describe(x, n, gender, dt, id);
}
private String display(HumanName name, String gender, DateType dob, Identifier id) {
StringBuilder b = new StringBuilder();
b.append(display(name));
b.append(" ");
b.append(gender);
b.append(" ");
b.append(display(dob));
if (id != null) {
b.append(" ( ");
b.append(display(id));
b.append(")");
}
return b.toString();
}
public void describe(XhtmlNode x, HumanName name, String gender, DateType dob, Identifier id) throws UnsupportedEncodingException, IOException {
render(x.b(), name);
x.tx(" ");
x.tx(gender);
x.tx(" ");
render(x, dob);
if (id != null) {
x.tx(" ( ");
render(x, id);
x.tx(")");
}
}
}

View File

@ -92,7 +92,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
}
@Override
public boolean render(XhtmlNode x, DomainResource r) throws FHIRFormatError, DefinitionException, IOException {
public boolean render(XhtmlNode x, Resource r) throws FHIRFormatError, DefinitionException, IOException {
return render(x, new DirectWrappers.ResourceWrapperDirect(context, r));
}
@ -109,7 +109,6 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
}
containedIds.clear();
generateByProfile(r, sd, r.root(), sd.getSnapshot().getElement(), ed, context.getProfileUtilities().getChildList(sd, ed), x, r.fhirType(), false, 0);
} catch (Exception e) {
e.printStackTrace();
x.para().b().style("color: maroon").tx("Exception generating Narrative: "+e.getMessage());
@ -121,6 +120,12 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
public String display(Resource r) throws UnsupportedEncodingException, IOException {
return "todo";
}
@Override
public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
return "Not done yet";
}
//
// public void inject(Element er, XhtmlNode x, NarrativeStatus status, boolean pretty) {
// if (!x.hasAttribute("xmlns"))

View File

@ -11,6 +11,7 @@ import org.hl7.fhir.r5.model.Reference;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
public class ProvenanceRenderer extends ResourceRenderer {
@ -19,7 +20,7 @@ public class ProvenanceRenderer extends ResourceRenderer {
super(context);
}
public boolean render(XhtmlNode x, DomainResource prv) throws UnsupportedEncodingException, IOException {
public boolean render(XhtmlNode x, Resource prv) throws UnsupportedEncodingException, IOException {
return render(x, (Provenance) prv);
}
@ -150,4 +151,9 @@ public class ProvenanceRenderer extends ResourceRenderer {
return "Provenance for "+displayReference(prv, prv.getTargetFirstRep());
}
@Override
public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
return "Not done yet";
}
}

View File

@ -42,7 +42,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
super(context);
}
public boolean render(XhtmlNode x, DomainResource q) throws UnsupportedEncodingException, IOException {
public boolean render(XhtmlNode x, Resource q) throws UnsupportedEncodingException, IOException {
return render(x, (Questionnaire) q);
}

View File

@ -44,7 +44,7 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer {
super(context);
}
public boolean render(XhtmlNode x, DomainResource q) throws UnsupportedEncodingException, IOException {
public boolean render(XhtmlNode x, Resource q) throws UnsupportedEncodingException, IOException {
return render(x, (QuestionnaireResponse) q);
}
@ -850,4 +850,9 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer {
return "todo";
}
@Override
public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
return "Not done yet";
}
}

View File

@ -0,0 +1,23 @@
package org.hl7.fhir.r5.renderers;
/**
* Rendering framework:
*
* * boolean render(DomainResource) : produce an HTML representation suitable for runtime / documentation, and insert it into the resource. Return true of any extensions encountered
* * boolean render(XhtmlNode, Resource: produce an HTML representation, and fill out the provided node with it. Return true of any extensions encountered
* * XhtmlNode build(DomainResource): same as render(DomainResource) but also return the XHtmlNode
*
* * String display(Base) : produce a plan text concise representation that serves to describe the resource
* * void display(XhtmlNode, Base) : produce a plan text concise representation that serves to describe the resource
*
* * void describe(XhtmlNode, Resource) : produce a short summary of the resource with key details presented (potentially more verbose than display, but still suitable for a single line)
*
* if not specific code for rendering a resource has been provided, and there's no liquid script to guide it, a generic rendering based onthe profile will be performed
*
* @author graha
*
*/
public class Renderer {
}

View File

@ -4,6 +4,7 @@ import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
public class RendererFactory {
@ -87,7 +88,7 @@ public class RendererFactory {
}
public static ResourceRenderer factory(ResourceWrapper resource, RenderingContext context) {
public static ResourceRenderer factory(ResourceWrapper resource, RenderingContext context, ResourceContext resourceContext) {
if (context.getTemplateProvider() != null) {
String liquidTemplate = context.getTemplateProvider().findTemplate(context, resource.getName());
if (liquidTemplate != null) {
@ -102,7 +103,11 @@ public class RendererFactory {
return new DiagnosticReportRenderer(context);
}
return new ProfileDrivenRenderer(context);
return new ProfileDrivenRenderer(context, resourceContext);
}
public static ResourceRenderer factory(ResourceWrapper rw, RenderingContext lrc) {
return factory(rw, lrc, null);
}

View File

@ -7,6 +7,7 @@ import org.apache.commons.lang3.NotImplementedException;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
@ -80,18 +81,23 @@ public abstract class ResourceRenderer extends DataRenderer {
return x;
}
public abstract boolean render(XhtmlNode x, DomainResource r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome;
public abstract boolean render(XhtmlNode x, Resource r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome;
public boolean render(XhtmlNode x, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
ProfileDrivenRenderer pr = new ProfileDrivenRenderer(context);
return pr.render(x, r);
}
public void describe(XhtmlNode x, DomainResource r) throws UnsupportedEncodingException, IOException {
public void describe(XhtmlNode x, Resource r) throws UnsupportedEncodingException, IOException {
x.tx(display(r));
}
public void describe(XhtmlNode x, ResourceWrapper r) throws UnsupportedEncodingException, IOException {
x.tx(display(r));
}
public abstract String display(Resource r) throws UnsupportedEncodingException, IOException;
public abstract String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException;
public static void inject(DomainResource r, XhtmlNode x, NarrativeStatus status) {
if (!x.hasAttribute("xmlns"))
@ -151,7 +157,12 @@ public abstract class ResourceRenderer extends DataRenderer {
new ProfileDrivenRenderer(context).generateResourceSummary(c, tr.getResource(), true, r.getReference().startsWith("#"));
}
} else if (tr != null && tr.getResource() != null) {
new ProfileDrivenRenderer(context).generateResourceSummary(c, tr.getResource(), r.getReference().startsWith("#"), r.getReference().startsWith("#"));
if (tr.getReference().startsWith("#")) {
// we already rendered this in this page
c.tx("See above ("+tr.getResource().fhirType()+"/"+tr.getResource().getId()+")");
} else {
new ProfileDrivenRenderer(context).generateResourceSummary(c, tr.getResource(), r.getReference().startsWith("#"), r.getReference().startsWith("#"));
}
} else {
c.addText(r.getReference());
}
@ -208,12 +219,13 @@ public abstract class ResourceRenderer extends DataRenderer {
org.hl7.fhir.r5.elementmodel.Element bundleElement = rcontext.resolveElement(url);
if (bundleElement != null) {
String bundleUrl = null;
if (bundleElement.getNamedChild("resource").getChildValue("id") != null) {
bundleUrl = "#" + bundleElement.fhirType().toLowerCase() + "_" + bundleElement.getNamedChild("resource").getChildValue("id");
Element br = bundleElement.getNamedChild("resource");
if (br.getChildValue("id") != null) {
bundleUrl = "#" + br.fhirType().toLowerCase() + "_" + br.getChildValue("id");
} else {
bundleUrl = "#" +fullUrlToAnchor(bundleElement.getChildValue("fullUrl"));
}
return new ResourceWithReference(bundleUrl, new ResourceWrapperMetaElement(this.context, bundleElement));
return new ResourceWithReference(bundleUrl, new ResourceWrapperMetaElement(this.context, br));
}
}
@ -254,6 +266,14 @@ public abstract class ResourceRenderer extends DataRenderer {
return null;
}
protected PropertyWrapper getProperty(BaseWrapper res, String name) {
for (PropertyWrapper t : res.children()) {
if (t.getName().equals(name))
return t;
}
return null;
}
protected boolean valued(PropertyWrapper pw) {
return pw != null && pw.hasValues();
}

View File

@ -9,6 +9,7 @@ import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
@ -22,7 +23,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
super(context, rcontext);
}
public boolean render(XhtmlNode x, DomainResource dr) throws FHIRFormatError, DefinitionException, IOException {
public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException {
return render(x, (StructureDefinition) dr);
}
@ -44,4 +45,14 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
return ((StructureDefinition) r).present();
}
public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
if (r.has("title")) {
return r.children("title").get(0).getBase().primitiveValue();
}
if (r.has("name")) {
return r.children("name").get(0).getBase().primitiveValue();
}
return "??";
}
}

View File

@ -23,6 +23,7 @@ import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
@ -44,6 +45,16 @@ public abstract class TerminologyRenderer extends ResourceRenderer {
return ((CanonicalResource) r).present();
}
public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
if (r.has("title")) {
return r.children("title").get(0).getBase().primitiveValue();
}
if (r.has("name")) {
return r.children("name").get(0).getBase().primitiveValue();
}
return "??";
}
protected class TargetElementComponentWrapper {
protected ConceptMapGroupComponent group;
protected TargetElementComponent comp;

View File

@ -59,7 +59,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
private List<ConceptMapRenderInstructions> renderingMaps = new ArrayList<ConceptMapRenderInstructions>();
public boolean render(XhtmlNode x, DomainResource dr) throws FHIRFormatError, DefinitionException, IOException {
public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException {
return render(x, (ValueSet) dr, false);
}

View File

@ -3,46 +3,58 @@ package org.hl7.fhir.r5.renderers.utils;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContextType;
import org.w3c.dom.Element;
public class Resolver {
public enum ResourceContextType {
PARAMETERS, BUNDLE
}
public interface IReferenceResolver {
ResourceWithReference resolve(RenderingContext context, String url);
}
public static class ResourceContext {
Bundle bundleResource;
org.hl7.fhir.r5.elementmodel.Element bundleElement;
private ResourceContextType type;
private Resource containerResource;
private org.hl7.fhir.r5.elementmodel.Element containerElement;
DomainResource resourceResource;
org.hl7.fhir.r5.elementmodel.Element resourceElement;
public ResourceContext(Bundle bundle, DomainResource dr) {
public ResourceContext(ResourceContextType type, Resource bundle, DomainResource dr) {
super();
this.bundleResource = bundle;
this.type = type;
this.containerResource = bundle;
this.resourceResource = dr;
}
public ResourceContext(org.hl7.fhir.r5.elementmodel.Element bundle, org.hl7.fhir.r5.elementmodel.Element dr) {
this.bundleElement = bundle;
public ResourceContext(ResourceContextType type, org.hl7.fhir.r5.elementmodel.Element bundle, org.hl7.fhir.r5.elementmodel.Element dr) {
super();
this.type = type;
this.containerElement = bundle;
this.resourceElement = dr;
}
public ResourceContext(Object bundle, Element doc) {
// TODO Auto-generated constructor stub
}
// public ResourceContext(Object bundle, Element doc) {
// // TODO Auto-generated constructor stub
// }
public Bundle getBundleResource() {
return bundleResource;
}
// public Bundle getBundleResource() {
// return containerResource;
// }
public org.hl7.fhir.r5.elementmodel.Element getBundleElement() {
return bundleElement;
}
// public org.hl7.fhir.r5.elementmodel.Element getBundleElement() {
// return containerElement;
// }
//
public DomainResource getResourceResource() {
return resourceResource;
}
@ -64,12 +76,26 @@ public class Resolver {
}
return null;
}
if (bundleResource != null) {
for (BundleEntryComponent be : bundleResource.getEntry()) {
if (be.getFullUrl().equals(value))
return be;
if (value.equals(be.getResource().fhirType()+"/"+be.getResource().getId()))
return be;
if (type == ResourceContextType.BUNDLE) {
if (containerResource != null) {
for (BundleEntryComponent be : ((Bundle) containerResource).getEntry()) {
if (be.getFullUrl().equals(value))
return be;
if (value.equals(be.getResource().fhirType()+"/"+be.getResource().getId()))
return be;
}
}
}
if (type == ResourceContextType.PARAMETERS) {
if (containerResource != null) {
for (ParametersParameterComponent p : ((Parameters) containerResource).getParameter()) {
if (p.getResource() != null && value.equals(p.getResource().fhirType()+"/"+p.getResource().getId())) {
BundleEntryComponent be = new BundleEntryComponent();
be.setResource(p.getResource());
return be;
}
}
}
}
return null;
@ -85,13 +111,24 @@ public class Resolver {
}
return null;
}
if (bundleElement != null) {
for (org.hl7.fhir.r5.elementmodel.Element be : bundleElement.getChildren("entry")) {
org.hl7.fhir.r5.elementmodel.Element res = be.getNamedChild("resource");
if (value.equals(be.getChildValue("fullUrl")))
return be;
if (value.equals(res.fhirType()+"/"+res.getChildValue("id")))
return be;
if (type == ResourceContextType.BUNDLE) {
if (containerElement != null) {
for (org.hl7.fhir.r5.elementmodel.Element be : containerElement.getChildren("entry")) {
org.hl7.fhir.r5.elementmodel.Element res = be.getNamedChild("resource");
if (value.equals(be.getChildValue("fullUrl")))
return be;
if (value.equals(res.fhirType()+"/"+res.getChildValue("id")))
return be;
}
}
}
if (type == ResourceContextType.PARAMETERS) {
if (containerElement != null) {
for (org.hl7.fhir.r5.elementmodel.Element p : containerElement.getChildren("parameter")) {
org.hl7.fhir.r5.elementmodel.Element res = p.getNamedChild("resource");
if (value.equals(res.fhirType()+"/"+res.getChildValue("id")))
return p;
}
}
}
return null;

View File

@ -71,7 +71,7 @@ public class FHIRLexer {
private String name;
public FHIRLexer(String source, String name) throws FHIRLexerException {
this.source = source;
this.source = source == null ? "" : source;
this.name = name == null ? "??" : name;
currentLocation = new SourceLocation(1, 1);
next();

View File

@ -1,6 +1,10 @@
package org.hl7.fhir.utilities;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.utilities.cache.NpmPackage;
/*
Copyright (c) 2011+, HL7, Inc.
@ -9,12 +13,12 @@ import org.hl7.fhir.exceptions.FHIRException;
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
@ -35,6 +39,22 @@ import org.hl7.fhir.exceptions.FHIRException;
public class VersionUtilities {
public static class VersionURLInfo {
private String version;
private String url;
public VersionURLInfo(String version, String url) {
super();
this.version = version;
this.url = url;
}
public String getVersion() {
return version;
}
public String getUrl() {
return url;
}
}
public static final String CURRENT_VERSION = "4.4";
public static final String CURRENT_FULL_VERSION = "4.4.0";
@ -225,4 +245,124 @@ public class VersionUtilities {
throw new FHIRException("Unknown version "+version);
}
public static VersionURLInfo parseVersionUrl(String url) {
if (url.length() < 24) {
return null;
}
String v = url.substring(20, 24);
if (v.endsWith("/")) {
v = v.substring(0, v.length()-1);
if (Utilities.existsInList(v, "1.0", "1.4", "3.0", "4.0", "5.0", CURRENT_VERSION)) {
return new VersionURLInfo(v, "http://hl7.org/fhir/"+url.substring(24));
}
}
return null;
}
public static List<String> getCanonicalResourceNames(String version) {
ArrayList<String> res = new ArrayList<String>();
if (isR2Ver(version) || isR2BVer(version)) {
res.add("ValueSet");
res.add("ConceptMap");
res.add("NamingSystem");
res.add("StructureDefinition");
res.add("DataElement");
res.add("Conformance");
res.add("OperationDefinition");
res.add("SearchParameter");
res.add("ImplementationGuide");
res.add("TestScript");
}
if (isR3Ver(version)) {
res.add("CapabilityStatement");
res.add("StructureDefinition");
res.add("ImplementationGuide");
res.add("SearchParameter");
res.add("MessageDefinition");
res.add("OperationDefinition");
res.add("CompartmentDefinition");
res.add("StructureMap");
res.add("GraphDefinition");
res.add("DataElement");
res.add("CodeSystem");
res.add("ValueSet");
res.add("ConceptMap");
res.add("ExpansionProfile");
res.add("Questionnaire");
res.add("ActivityDefinition");
res.add("ServiceDefinition");
res.add("PlanDefinition");
res.add("Measure");
res.add("TestScript");
}
if (isR4Ver(version)) {
res.add("ActivityDefinition");
res.add("CapabilityStatement");
res.add("ChargeItemDefinition");
res.add("CodeSystem");
res.add("CompartmentDefinition");
res.add("ConceptMap");
res.add("EffectEvidenceSynthesis");
res.add("EventDefinition");
res.add("Evidence");
res.add("EvidenceVariable");
res.add("ExampleScenario");
res.add("GraphDefinition");
res.add("ImplementationGuide");
res.add("Library");
res.add("Measure");
res.add("MessageDefinition");
res.add("NamingSystem");
res.add("OperationDefinition");
res.add("PlanDefinition");
res.add("Questionnaire");
res.add("ResearchDefinition");
res.add("ResearchElementDefinition");
res.add("RiskEvidenceSynthesis");
res.add("SearchParameter");
res.add("StructureDefinition");
res.add("StructureMap");
res.add("TerminologyCapabilities");
res.add("TestScript");
res.add("ValueSet");
}
if (isR5Ver(version)) {
res.add("ActivityDefinition");
res.add("CapabilityStatement");
res.add("CapabilityStatement2");
res.add("ChargeItemDefinition");
res.add("Citation");
res.add("CodeSystem");
res.add("CompartmentDefinition");
res.add("ConceptMap");
res.add("ConditionDefinition");
res.add("EventDefinition");
res.add("Evidence");
res.add("EvidenceReport");
res.add("EvidenceVariable");
res.add("ExampleScenario");
res.add("GraphDefinition");
res.add("ImplementationGuide");
res.add("Library");
res.add("Measure");
res.add("MessageDefinition");
res.add("NamingSystem");
res.add("OperationDefinition");
res.add("PlanDefinition");
res.add("Questionnaire");
res.add("SearchParameter");
res.add("StructureDefinition");
res.add("StructureMap");
res.add("TerminologyCapabilities");
res.add("TestScript");
res.add("ValueSet");
}
return res;
}
}

View File

@ -1058,5 +1058,25 @@ public class NpmPackage {
return "fhir.core".equals(JSONUtil.str(npm, "type"));
}
public boolean hasCanonical(String url) {
if (url == null) {
return false;
}
String u = url.contains("|") ? url.substring(0, url.indexOf("|")) : url;
String v = url.contains("|") ? url.substring(url.indexOf("|")+1) : null;
NpmPackageFolder folder = folders.get("package");
if (folder != null) {
for (JsonElement e : folder.index.getAsJsonArray("files")) {
JsonObject o = (JsonObject) e;
if (u.equals(JSONUtil.str(o, "url"))) {
if (v == null || v.equals(JSONUtil.str(o, "version"))) {
return true;
}
}
}
}
return false;
}
}

View File

@ -25,6 +25,7 @@ public class I18nConstants {
public static final String BUNDLE_BUNDLE_ENTRY_NOFIRSTRESOURCE = "Bundle_BUNDLE_Entry_NoFirstResource";
public static final String BUNDLE_BUNDLE_ENTRY_NOFULLURL = "Bundle_BUNDLE_Entry_NoFullUrl";
public static final String BUNDLE_BUNDLE_ENTRY_NOPROFILE = "Bundle_BUNDLE_Entry_NoProfile";
public static final String BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES = "BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES";
public static final String BUNDLE_BUNDLE_ENTRY_NOTFOUND = "Bundle_BUNDLE_Entry_NotFound";
public static final String BUNDLE_BUNDLE_ENTRY_ORPHAN = "Bundle_BUNDLE_Entry_Orphan";
public static final String BUNDLE_BUNDLE_ENTRY_TYPE = "Bundle_BUNDLE_Entry_Type";
@ -543,10 +544,14 @@ public class I18nConstants {
public static final String VALIDATION_VAL_PROFILE_SIGNPOST_GLOBAL = "VALIDATION_VAL_PROFILE_SIGNPOST_GLOBAL";
public static final String VALIDATION_VAL_PROFILE_SIGNPOST_META = "VALIDATION_VAL_PROFILE_SIGNPOST_META";
public static final String VALIDATION_VAL_PROFILE_SLICEORDER = "Validation_VAL_Profile_SliceOrder";
public static final String VALIDATION_VAL_PROFILE_OTHER_VERSION = "VALIDATION_VAL_PROFILE_OTHER_VERSION";
public static final String VALIDATION_VAL_PROFILE_THIS_VERSION_OK = "VALIDATION_VAL_PROFILE_THIS_VERSION_OK";
public static final String VALIDATION_VAL_PROFILE_THIS_VERSION_OTHER = "VALIDATION_VAL_PROFILE_THIS_VERSION_OTHER";
public static final String VALIDATION_VAL_PROFILE_UNKNOWN = "Validation_VAL_Profile_Unknown";
public static final String VALIDATION_VAL_PROFILE_WRONGTYPE = "Validation_VAL_Profile_WrongType";
public static final String VALIDATION_VAL_PROFILE_WRONGTYPE2 = "Validation_VAL_Profile_WrongType2";
public static final String VALIDATION_VAL_UNKNOWN_PROFILE = "Validation_VAL_Unknown_Profile";
public static final String VALUESET_INCLUDE_INVALID_CONCEPT_CODE = "VALUESET_INCLUDE_INVALID_CONCEPT_CODE";
public static final String VALUESET_INCLUDE_INVALID_CONCEPT_CODE_VER = "VALUESET_INCLUDE_INVALID_CONCEPT_CODE_VER";
public static final String VALUESET_NO_SYSTEM_WARNING = "VALUESET_NO_SYSTEM_WARNING";

View File

@ -571,3 +571,7 @@ PACKAGE_VERSION_MISMATCH = FHIR Version mismatch in package {0}: version is {2}
VALUESET_REFERENCE_UNKNOWN = The value set import {0} could not be found so cannot be checked
VALUESET_REFERENCE_INVALID_TYPE = The value set import {0} points to a resource of type {1} which is not valid
SD_MUST_HAVE_DERIVATION = StructureDefinition {0} must have a derivation, since it has a baseDefinition
VALIDATION_VAL_PROFILE_OTHER_VERSION = Profile is for a different version of FHIR ({0}) so has been ignored
VALIDATION_VAL_PROFILE_THIS_VERSION_OK = Profile for this version of FHIR - all OK
VALIDATION_VAL_PROFILE_THIS_VERSION_OTHER = Profile is for this version of FHIR, but is an invalid type {0}
BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES = Multiple profiles found for contained resource. This is not supported at this time. (Type {0}: {1})

View File

@ -58,7 +58,7 @@ public class CachingPackageClientTests {
Assertions.assertTrue(client.exists("hl7.fhir.r4.core", "4.0.1"));
Assertions.assertTrue(!client.exists("hl7.fhir.r4.core", "1.0.2"));
Assertions.assertTrue(!client.exists("hl7.fhir.nothing", "1.0.1"));
Assertions.assertTrue(client.exists("hl7.fhir.pubpack", "0.0.6"));
Assertions.assertTrue(client.exists("hl7.fhir.pubpack", "0.0.7"));
}
@Test

View File

@ -22,7 +22,7 @@ public class PackageCacheTests {
System.out.println("remaining packages: "+list.toString());
}
Assertions.assertTrue(list.isEmpty(), "List should be true but is "+list.toString());
NpmPackage npm = cache.loadPackage("hl7.fhir.pubpack", "0.0.3");
NpmPackage npm = cache.loadPackage("hl7.fhir.pubpack", "0.0.7");
npm.loadAllFiles();
Assertions.assertNotNull(npm);
File dir = new File(Utilities.path("[tmp]", "cache"));

View File

@ -34,6 +34,7 @@ import org.hl7.fhir.r5.utils.*;
import org.hl7.fhir.r5.utils.IResourceValidator.*;
import org.hl7.fhir.r5.utils.StructureMapUtilities.ITransformerServices;
import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.validation.cli.services.StandAloneValidatorFetcher.IPackageInstaller;
import org.hl7.fhir.validation.instance.InstanceValidator;
import org.hl7.fhir.utilities.IniFile;
import org.hl7.fhir.utilities.TextFile;
@ -163,7 +164,42 @@ POSSIBILITY OF SUCH DAMAGE.
* @author Grahame Grieve
*
*/
public class ValidationEngine implements IValidatorResourceFetcher {
public class ValidationEngine implements IValidatorResourceFetcher, IPackageInstaller {
public static class VersionSourceInformation {
private List<String> report = new ArrayList<>();
private List<String> versions = new ArrayList<>();
public void see(String version, String src) {
version = VersionUtilities.getMajMin(version);
report.add(src+": "+version);
if (!versions.contains(version)) {
versions.add(version);
Collections.sort(versions);
}
}
public boolean isEmpty() {
return versions.isEmpty();
}
public int size() {
return versions.size();
}
public String version() {
return versions.get(0);
}
public List<String> getReport() {
if (report.isEmpty()) {
report.add("(nothing found)");
}
return report;
}
}
public class ScanOutputItem {
private String ref;
@ -311,9 +347,10 @@ public class ValidationEngine implements IValidatorResourceFetcher {
public ValidationEngine() throws IOException {
pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION);
context = SimpleWorkerContext.fromNothing();
}
public void setTerminologyServer(String src, String log, FhirPublication version) throws Exception {
public void setTerminologyServer(String src, String log, FhirPublication version) throws FHIRException, URISyntaxException {
connectToTSServer(src, log, version);
}
@ -342,7 +379,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
this.showTimes = showTimes;
}
public ValidationEngine(String src, String txsrvr, String txLog, FhirPublication version, boolean canRunWithoutTerminologyServer, String vString) throws Exception {
public ValidationEngine(String src, String txsrvr, String txLog, FhirPublication version, boolean canRunWithoutTerminologyServer, String vString) throws FHIRException, IOException, URISyntaxException {
pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION);
loadCoreDefinitions(src, false);
context.setCanRunWithoutTerminology(canRunWithoutTerminologyServer);
@ -350,14 +387,14 @@ public class ValidationEngine implements IValidatorResourceFetcher {
this.version = vString;
}
public ValidationEngine(String src, String txsrvr, String txLog, FhirPublication version, String vString) throws Exception {
public ValidationEngine(String src, String txsrvr, String txLog, FhirPublication version, String vString) throws FHIRException, IOException, URISyntaxException {
pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION);
loadCoreDefinitions(src, false);
setTerminologyServer(txsrvr, txLog, version);
this.version = vString;
}
public ValidationEngine(String src) throws Exception {
public ValidationEngine(String src) throws FHIRException, IOException {
loadCoreDefinitions(src, false);
pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION);
}
@ -370,7 +407,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
this.language = language;
}
private void loadCoreDefinitions(String src, boolean recursive) throws Exception {
private void loadCoreDefinitions(String src, boolean recursive) throws FHIRException, IOException {
if (pcm == null) {
pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION);
}
@ -435,7 +472,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
return ep;
}
private byte[] loadProfileSource(String src) throws Exception {
private byte[] loadProfileSource(String src) throws FHIRException, FileNotFoundException, IOException {
if (Utilities.noString(src)) {
throw new FHIRException("Profile Source '" + src + "' could not be processed");
} else if (src.startsWith("https:") || src.startsWith("http:")) {
@ -447,13 +484,13 @@ public class ValidationEngine implements IValidatorResourceFetcher {
}
}
private byte[] loadProfileFromUrl(String src) throws Exception {
private byte[] loadProfileFromUrl(String src) throws FHIRException {
try {
URL url = new URL(src+"?nocache=" + System.currentTimeMillis());
URLConnection c = url.openConnection();
return IOUtils.toByteArray(c.getInputStream());
} catch (Exception e) {
throw new Exception("Unable to find definitions at URL '"+src+"': "+e.getMessage(), e);
throw new FHIRException("Unable to find definitions at URL '"+src+"': "+e.getMessage(), e);
}
}
@ -464,9 +501,10 @@ public class ValidationEngine implements IValidatorResourceFetcher {
return TextFile.fileToBytes(src);
}
/** explore should be true if we're trying to load an -ig parameter, and false if we're loading source **/
/** explore should be true if we're trying to load an -ig parameter, and false if we're loading source
* @throws IOException **/
private Map<String, byte[]> loadIgSource(String src, boolean recursive, boolean explore) throws Exception {
private Map<String, byte[]> loadIgSource(String src, boolean recursive, boolean explore) throws FHIRException, IOException {
// src can be one of the following:
// - a canonical url for an ig - this will be converted to a package id and loaded into the cache
// - a package id for an ig - this will be loaded into the cache
@ -510,11 +548,60 @@ public class ValidationEngine implements IValidatorResourceFetcher {
} else if ((src.matches(FilesystemPackageCacheManager.PACKAGE_REGEX) || src.matches(FilesystemPackageCacheManager.PACKAGE_VERSION_REGEX)) && !src.endsWith(".zip") && !src.endsWith(".tgz")) {
return fetchByPackage(src);
}
throw new Exception("Unable to find/resolve/read -ig "+src);
throw new FHIRException("Unable to find/resolve/read -ig "+src);
}
private Map<String, byte[]> loadIgSourceForVersion(String src, boolean recursive, boolean explore, VersionSourceInformation versions) throws FHIRException, IOException {
if (src.startsWith("https:") || src.startsWith("http:")) {
String v = null;
if (src.contains("|")) {
v = src.substring(src.indexOf("|")+1);
src = src.substring(0, src.indexOf("|"));
}
String pid = pcm.getPackageId(src);
if (!Utilities.noString(pid)) {
versions.see(fetchVersionByPackage(pid+(v == null ? "" : "#"+v)), "Package "+src);
return null;
} else {
return fetchVersionFromUrl(src+(v == null ? "" : "|"+v), explore, versions);
}
}
File f = new File(Utilities.path(src));
if (f.exists()) {
if (f.isDirectory() && new File(Utilities.path(src, "package.tgz")).exists()) {
versions.see(loadPackageForVersion(new FileInputStream(Utilities.path(src, "package.tgz")), Utilities.path(src, "package.tgz")), "Package "+src);
return null;
}
if (f.isDirectory() && new File(Utilities.path(src, "igpack.zip")).exists())
return readZip(new FileInputStream(Utilities.path(src, "igpack.zip")));
if (f.isDirectory() && new File(Utilities.path(src, "validator.pack")).exists())
return readZip(new FileInputStream(Utilities.path(src, "validator.pack")));
if (f.isDirectory())
return scanDirectory(f, recursive);
if (src.endsWith(".tgz")) {
versions.see(loadPackageForVersion(new FileInputStream(src), src), "Package "+src);
return null;
}
if (src.endsWith(".pack"))
return readZip(new FileInputStream(src));
if (src.endsWith("igpack.zip"))
return readZip(new FileInputStream(src));
FhirFormat fmt = checkIsResource(src);
if (fmt != null) {
Map<String, byte[]> res = new HashMap<String, byte[]>();
res.put(Utilities.changeFileExt(src, "."+fmt.getExtension()), TextFile.fileToBytesNCS(src));
return res;
}
} else if ((src.matches(FilesystemPackageCacheManager.PACKAGE_REGEX) || src.matches(FilesystemPackageCacheManager.PACKAGE_VERSION_REGEX)) && !src.endsWith(".zip") && !src.endsWith(".tgz")) {
versions.see(fetchVersionByPackage(src), "Package "+src);
return null;
}
throw new FHIRException("Unable to find/resolve/read -ig "+src);
}
private Map<String, byte[]> fetchFromUrl(String src, boolean explore) throws Exception {
private Map<String, byte[]> fetchFromUrl(String src, boolean explore) throws FHIRException, IOException {
if (src.endsWith(".tgz"))
return loadPackage(fetchFromUrlSpecific(src, false), src);
if (src.endsWith(".pack"))
@ -551,15 +638,59 @@ public class ValidationEngine implements IValidatorResourceFetcher {
res.put(Utilities.changeFileExt(src, "."+fmt.getExtension()), cnt);
return res;
}
throw new Exception("Unable to find/resolve/read -ig "+src);
throw new FHIRException("Unable to find/resolve/read -ig "+src);
}
private InputStream fetchFromUrlSpecific(String source, boolean optional) throws Exception {
private Map<String, byte[]> fetchVersionFromUrl(String src, boolean explore, VersionSourceInformation versions) throws FHIRException, IOException {
if (src.endsWith(".tgz")) {
versions.see(loadPackageForVersion(fetchFromUrlSpecific(src, false), src), "From Package "+src);
return null;
}
if (src.endsWith(".pack"))
return readZip(fetchFromUrlSpecific(src, false));
if (src.endsWith("igpack.zip"))
return readZip(fetchFromUrlSpecific(src, false));
InputStream stream = null;
if (explore) {
stream = fetchFromUrlSpecific(Utilities.pathURL(src, "package.tgz"), true);
if (stream != null) {
versions.see(loadPackageForVersion(stream, Utilities.pathURL(src, "package.tgz")), "From Package at "+src);
return null;
}
// todo: these options are deprecated - remove once all IGs have been rebuilt post R4 technical correction
stream = fetchFromUrlSpecific(Utilities.pathURL(src, "igpack.zip"), true);
if (stream != null)
return readZip(stream);
stream = fetchFromUrlSpecific(Utilities.pathURL(src, "validator.pack"), true);
if (stream != null)
return readZip(stream);
stream = fetchFromUrlSpecific(Utilities.pathURL(src, "validator.pack"), true);
//// -----
}
// ok, having tried all that... now we'll just try to access it directly
byte[] cnt;
if (stream == null)
cnt = fetchFromUrlSpecific(src, "application/json", true);
else
cnt = TextFile.streamToBytes(stream);
FhirFormat fmt = checkIsResource(cnt, src);
if (fmt != null) {
Map<String, byte[]> res = new HashMap<String, byte[]>();
res.put(Utilities.changeFileExt(src, "."+fmt.getExtension()), cnt);
return res;
}
throw new FHIRException("Unable to find/resolve/read -ig "+src);
}
private InputStream fetchFromUrlSpecific(String source, boolean optional) throws FHIRException, IOException {
try {
URL url = new URL(source+"?nocache=" + System.currentTimeMillis());
URLConnection c = url.openConnection();
return c.getInputStream();
} catch (Exception e) {
} catch (IOException e) {
if (optional)
return null;
else
@ -567,13 +698,13 @@ public class ValidationEngine implements IValidatorResourceFetcher {
}
}
private byte[] fetchFromUrlSpecific(String source, String contentType, boolean optional) throws Exception {
private byte[] fetchFromUrlSpecific(String source, String contentType, boolean optional) throws FHIRException, IOException {
try {
URL url = new URL(source+"?nocache=" + System.currentTimeMillis());
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Accept", contentType);
return TextFile.streamToBytes(conn.getInputStream());
} catch (Exception e) {
} catch (IOException e) {
if (optional)
return null;
else
@ -604,11 +735,15 @@ public class ValidationEngine implements IValidatorResourceFetcher {
return Utilities.existsInList(Utilities.getFileExtension(ff.getName()).toLowerCase(), "md", "css", "js", "png", "gif", "jpg", "html", "tgz", "pack", "zip");
}
private Map<String, byte[]> loadPackage(InputStream stream, String name) throws Exception {
private Map<String, byte[]> loadPackage(InputStream stream, String name) throws FHIRException, IOException {
return loadPackage(NpmPackage.fromPackage(stream));
}
public Map<String, byte[]> loadPackage(NpmPackage pi) throws Exception {
private String loadPackageForVersion(InputStream stream, String name) throws FHIRException, IOException {
return NpmPackage.fromPackage(stream).fhirVersion();
}
public Map<String, byte[]> loadPackage(NpmPackage pi) throws FHIRException, IOException {
context.getLoadedPackages().add(pi.name()+"#"+pi.version());
Map<String, byte[]> res = new HashMap<String, byte[]>();
for (String s : pi.dependencies()) {
@ -652,7 +787,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
System.out.println(message);
}
private Map<String, byte[]> fetchByPackage(String src) throws Exception {
private Map<String, byte[]> fetchByPackage(String src) throws FHIRException, IOException {
String id = src;
String version = null;
if (src.contains("#")) {
@ -679,13 +814,46 @@ public class ValidationEngine implements IValidatorResourceFetcher {
return loadPackage(pi);
}
private Map<String, byte[]> resolvePackage(String id, String v) throws Exception {
private String fetchVersionByPackage(String src) throws FHIRException, IOException {
String id = src;
String version = null;
if (src.contains("#")) {
id = src.substring(0, src.indexOf("#"));
version = src.substring(src.indexOf("#")+1);
}
if (pcm == null) {
log("Creating Package manager?");
pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION);
}
if (version == null) {
version = pcm.getLatestVersion(id);
}
NpmPackage pi = null;
if (version == null) {
pi = pcm.loadPackageFromCacheOnly(id);
if (pi != null)
log(" ... Using version "+pi.version());
} else
pi = pcm.loadPackageFromCacheOnly(id, version);
if (pi == null) {
return resolvePackageForVersion(id, version);
} else {
return pi.fhirVersion();
}
}
private Map<String, byte[]> resolvePackage(String id, String v) throws FHIRException, IOException {
NpmPackage pi = pcm.loadPackage(id, v);
if (pi != null && v == null)
log(" ... Using version "+pi.version());
return loadPackage(pi);
}
private String resolvePackageForVersion(String id, String v) throws FHIRException, IOException {
NpmPackage pi = pcm.loadPackage(id, v);
return pi.fhirVersion();
}
public SimpleWorkerContext getContext() {
return context;
}
@ -758,7 +926,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
}
}
public void loadProfile(String src) throws Exception {
public void loadProfile(String src) throws FHIRException, IOException {
if (context.hasResource(StructureDefinition.class, src))
return;
if (context.hasResource(ImplementationGuide.class, src))
@ -770,7 +938,13 @@ public class ValidationEngine implements IValidatorResourceFetcher {
context.cacheResource(r);
}
public void loadIg(String src, boolean recursive) throws IOException, FHIRException, Exception {
public void scanForIgVersion(String src, boolean recursive, VersionSourceInformation versions) throws IOException, FHIRException, Exception {
Map<String, byte[]> source = loadIgSourceForVersion(src, recursive, true, versions);
if (source != null && source.containsKey("version.info"))
versions.see(readInfoVersion(source.get("version.info")), "version.info in "+src);
}
public void loadIg(String src, boolean recursive) throws IOException, FHIRException {
NpmPackage npm = src.matches(FilesystemPackageCacheManager.PACKAGE_REGEX) ? pcm.loadPackage(src, null) : null;
if (npm != null) {
for (String s : npm.dependencies()) {
@ -834,7 +1008,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
return r;
}
public Resource loadResourceByVersion(String version, byte[] content, String fn) throws IOException, Exception {
public Resource loadResourceByVersion(String version, byte[] content, String fn) throws IOException, FHIRException {
Resource r;
if (version.startsWith("3.0")) {
org.hl7.fhir.dstu3.model.Resource res;
@ -845,7 +1019,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
else if (fn.endsWith(".txt") || fn.endsWith(".map") )
res = new org.hl7.fhir.dstu3.utils.StructureMapUtilities(null).parse(new String(content));
else
throw new Exception("Unsupported format for "+fn);
throw new FHIRException("Unsupported format for "+fn);
r = VersionConvertor_30_50.convertResource(res, false);
} else if (version.startsWith("4.0")) {
org.hl7.fhir.r4.model.Resource res;
@ -856,7 +1030,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
else if (fn.endsWith(".txt") || fn.endsWith(".map") )
res = new org.hl7.fhir.r4.utils.StructureMapUtilities(null).parse(new String(content), fn);
else
throw new Exception("Unsupported format for "+fn);
throw new FHIRException("Unsupported format for "+fn);
r = VersionConvertor_40_50.convertResource(res);
} else if (version.startsWith("1.4")) {
org.hl7.fhir.dstu2016may.model.Resource res;
@ -865,7 +1039,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
else if (fn.endsWith(".json") && !fn.endsWith("template.json"))
res = new org.hl7.fhir.dstu2016may.formats.JsonParser().parse(new ByteArrayInputStream(content));
else
throw new Exception("Unsupported format for "+fn);
throw new FHIRException("Unsupported format for "+fn);
r = VersionConvertor_14_50.convertResource(res);
} else if (version.startsWith("1.0")) {
org.hl7.fhir.dstu2.model.Resource res;
@ -874,7 +1048,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
else if (fn.endsWith(".json") && !fn.endsWith("template.json"))
res = new org.hl7.fhir.dstu2.formats.JsonParser().parse(new ByteArrayInputStream(content));
else
throw new Exception("Unsupported format for "+fn);
throw new FHIRException("Unsupported format for "+fn);
VersionConvertorAdvisor50 advisor = new org.hl7.fhir.convertors.misc.IGR2ConvertorAdvisor5();
r = VersionConvertor_10_50.convertResource(res, advisor);
} else if (version.equals(Constants.VERSION) || "current".equals(version)) {
@ -887,9 +1061,9 @@ public class ValidationEngine implements IValidatorResourceFetcher {
else if (fn.endsWith(".txt") || fn.endsWith(".map") )
r = new org.hl7.fhir.r5.utils.StructureMapUtilities(null).parse(new String(content), fn);
else
throw new Exception("Unsupported format for "+fn);
throw new FHIRException("Unsupported format for "+fn);
} else
throw new Exception("Unsupported version "+version);
throw new FHIRException("Unsupported version "+version);
return r;
}
@ -923,11 +1097,11 @@ public class ValidationEngine implements IValidatorResourceFetcher {
FhirFormat cntType = null;
}
public Content loadContent(String source, String opName) throws Exception {
public Content loadContent(String source, String opName) throws FHIRException, IOException {
Map<String, byte[]> s = loadIgSource(source, false, false);
Content res = new Content();
if (s.size() != 1)
throw new Exception("Unable to find resource " + source + " to "+opName);
throw new FHIRException("Unable to find resource " + source + " to "+opName);
for (Entry<String, byte[]> t: s.entrySet()) {
res.focus = t.getValue();
if (t.getKey().endsWith(".json"))
@ -939,13 +1113,13 @@ public class ValidationEngine implements IValidatorResourceFetcher {
else if (t.getKey().endsWith(".txt") || t.getKey().endsWith(".map"))
res.cntType = FhirFormat.TEXT;
else
throw new Exception("Todo: Determining resource type is not yet done");
throw new FHIRException("Todo: Determining resource type is not yet done");
}
return res;
}
// testing entry point
public OperationOutcome validate(FhirFormat format, InputStream stream, List<String> profiles) throws Exception {
public OperationOutcome validate(FhirFormat format, InputStream stream, List<String> profiles) throws FHIRException, IOException, EOperationOutcome {
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
InstanceValidator validator = getValidator();
validator.validate(null, messages, stream, format, asSdList(profiles));
@ -966,13 +1140,13 @@ public class ValidationEngine implements IValidatorResourceFetcher {
return list;
}
public OperationOutcome validate(String source, List<String> profiles) throws Exception {
public OperationOutcome validate(String source, List<String> profiles) throws FHIRException, IOException {
List<String> l = new ArrayList<String>();
l.add(source);
return (OperationOutcome)validate(l, profiles);
}
public List<ScanOutputItem> validateScan(List<String> sources, Set<String> guides) throws Exception {
public List<ScanOutputItem> validateScan(List<String> sources, Set<String> guides) throws FHIRException, IOException, EOperationOutcome {
List<String> refs = new ArrayList<String>();
handleSources(sources, refs);
@ -1048,7 +1222,28 @@ public class ValidationEngine implements IValidatorResourceFetcher {
return null;
}
public Resource validate(List<String> sources, List<String> profiles) throws Exception {
public void scanForVersions(List<String> sources, VersionSourceInformation versions) throws FHIRException, IOException {
List<String> refs = new ArrayList<String>();
handleSources(sources, refs);
for (String ref : refs) {
Content cnt = loadContent(ref, "validate");
String s = TextFile.bytesToString(cnt.focus);
if (s.contains("http://hl7.org/fhir/3.0")) {
versions.see("3.0", "Profile in "+ref);
}
if (s.contains("http://hl7.org/fhir/1.0")) {
versions.see("1.0", "Profile in "+ref);
}
if (s.contains("http://hl7.org/fhir/4.0")) {
versions.see("4.0", "Profile in "+ref);
}
if (s.contains("http://hl7.org/fhir/1.4")) {
versions.see("1.4", "Profile in "+ref);
}
}
}
public Resource validate(List<String> sources, List<String> profiles) throws FHIRException, IOException {
List<String> refs = new ArrayList<String>();
boolean asBundle = handleSources(sources, refs);
Bundle results = new Bundle();
@ -1065,7 +1260,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
results.addEntry().setResource(outcome);
} catch (Exception e) {
System.out.println("Validation Infrastructure fail validating "+ref+": "+e.getMessage());
throw e;
throw new FHIRException(e);
}
}
if (asBundle)
@ -1082,7 +1277,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
}
}
public OperationOutcome validateString(String location, String source, FhirFormat format, List<String> profiles) throws Exception {
public OperationOutcome validateString(String location, String source, FhirFormat format, List<String> profiles) throws FHIRException, IOException, EOperationOutcome, SAXException {
return validate(location, source.getBytes(), format, profiles);
}
@ -1136,14 +1331,14 @@ public class ValidationEngine implements IValidatorResourceFetcher {
return isBundle;
}
public OperationOutcome validate(byte[] source, FhirFormat cntType, List<String> profiles) throws Exception {
public OperationOutcome validate(byte[] source, FhirFormat cntType, List<String> profiles) throws FHIRException, IOException, EOperationOutcome {
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
InstanceValidator validator = getValidator();
validator.validate(null, messages, new ByteArrayInputStream(source), cntType, asSdList(profiles));
return messagesToOutcome(messages);
}
public OperationOutcome validate(String location, byte[] source, FhirFormat cntType, List<String> profiles) throws Exception {
public OperationOutcome validate(String location, byte[] source, FhirFormat cntType, List<String> profiles) throws FHIRException, IOException, EOperationOutcome, SAXException {
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
if (doNative) {
if (cntType == FhirFormat.JSON)
@ -1161,7 +1356,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
return messagesToOutcome(messages);
}
public OperationOutcome validate(String location, byte[] source, FhirFormat cntType, List<String> profiles, IdStatus resourceIdRule, boolean anyExtensionsAllowed, BestPracticeWarningLevel bpWarnings, CheckDisplayOption displayOption) throws Exception {
public OperationOutcome validate(String location, byte[] source, FhirFormat cntType, List<String> profiles, IdStatus resourceIdRule, boolean anyExtensionsAllowed, BestPracticeWarningLevel bpWarnings, CheckDisplayOption displayOption) throws FHIRException, IOException, EOperationOutcome, SAXException {
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
if (doNative) {
if (cntType == FhirFormat.JSON)
@ -1254,12 +1449,12 @@ public class ValidationEngine implements IValidatorResourceFetcher {
return issue.getSeverity().toString()+" @ "+issue.getLocation() + " " +issue.getDetails().getText() +(source != null ? " (src = "+source+")" : "");
}
public org.hl7.fhir.r5.elementmodel.Element transform(String source, String map) throws Exception {
public org.hl7.fhir.r5.elementmodel.Element transform(String source, String map) throws FHIRException, IOException {
Content cnt = loadContent(source, "validate");
return transform(cnt.focus, cnt.cntType, map);
}
public org.hl7.fhir.r5.elementmodel.Element transform(byte[] source, FhirFormat cntType, String mapUri) throws Exception {
public org.hl7.fhir.r5.elementmodel.Element transform(byte[] source, FhirFormat cntType, String mapUri) throws FHIRException, IOException {
List<Base> outputs = new ArrayList<Base>();
StructureMapUtilities scu = new StructureMapUtilities(context, new TransformSupportServices(outputs));
@ -1299,7 +1494,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
return Manager.build(getContext(), structureDefinition);
}
public DomainResource generate(String source, String version) throws Exception {
public DomainResource generate(String source, String version) throws FHIRException, IOException, EOperationOutcome {
Content cnt = loadContent(source, "validate");
Resource res = loadResourceByVersion(version, cnt.focus, source);
RenderingContext rc = new RenderingContext(context, null, null, "http://hl7.org/fhir", "", null, ResourceRendererMode.RESOURCE);
@ -1307,25 +1502,25 @@ public class ValidationEngine implements IValidatorResourceFetcher {
return (DomainResource) res;
}
public void convert(String source, String output) throws Exception {
public void convert(String source, String output) throws FHIRException, IOException {
Content cnt = loadContent(source, "validate");
Element e = Manager.parse(context, new ByteArrayInputStream(cnt.focus), cnt.cntType);
Manager.compose(context, e, new FileOutputStream(output), (output.endsWith(".json") ? FhirFormat.JSON : FhirFormat.XML), OutputStyle.PRETTY, null);
}
public String evaluateFhirPath(String source, String expression) throws Exception {
public String evaluateFhirPath(String source, String expression) throws FHIRException, IOException {
Content cnt = loadContent(source, "validate");
FHIRPathEngine fpe = new FHIRPathEngine(context);
Element e = Manager.parse(context, new ByteArrayInputStream(cnt.focus), cnt.cntType);
return fpe.evaluateToString(e, expression);
}
public StructureDefinition snapshot(String source, String version) throws Exception {
public StructureDefinition snapshot(String source, String version) throws FHIRException, IOException {
Content cnt = loadContent(source, "validate");
Resource res = loadResourceByVersion(version, cnt.focus, Utilities.getFileNameForName(source));
if (!(res instanceof StructureDefinition))
throw new Exception("Require a StructureDefinition for generating a snapshot");
throw new FHIRException("Require a StructureDefinition for generating a snapshot");
StructureDefinition sd = (StructureDefinition) res;
StructureDefinition base = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
@ -1670,8 +1865,9 @@ public class ValidationEngine implements IValidatorResourceFetcher {
@Override
public boolean resolveURL(Object appContext, String path, String url) throws IOException, FHIRException {
if (!url.startsWith("http://hl7.org/fhir"))
return true; // we don't bother with those.
if (!url.startsWith("http://") && !url.startsWith("https://")) { // ignore these
return true;
}
if (context.fetchResource(Resource.class, url) != null)
return true;
if (Utilities.existsInList(url, "http://hl7.org/fhir/sid/us-ssn", "http://hl7.org/fhir/sid/cvx", "http://hl7.org/fhir/sid/ndc", "http://hl7.org/fhir/sid/us-npi", "http://hl7.org/fhir/sid/icd-10",
@ -1679,6 +1875,9 @@ public class ValidationEngine implements IValidatorResourceFetcher {
"http://hl7.org/fhir/workflow", "http://hl7.org/fhir/ConsentPolicy/opt-out", "http://hl7.org/fhir/ConsentPolicy/opt-in")) {
return true;
}
if (Utilities.existsInList(url, "http://loinc.org", "http://unitsofmeasure.org", "http://snomed.info/sct")) {
return true;
}
if (fetcher != null) {
return fetcher.resolveURL(appContext, path, url);
};
@ -1690,7 +1889,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
this.locale = locale;
}
public void handleOutput(Resource r, String output, String version) throws Exception {
public void handleOutput(Resource r, String output, String version) throws FHIRException, IOException {
if (output.startsWith("http://") || output.startsWith("http://")) {
ByteArrayOutputStream bs = new ByteArrayOutputStream();
handleOutputToStream(r, output, bs, version);
@ -1719,7 +1918,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
}
}
private void handleOutputToStream(Resource r, String fn, OutputStream s, String version) throws Exception {
private void handleOutputToStream(Resource r, String fn, OutputStream s, String version) throws FHIRException, IOException {
if (fn.endsWith(".html") || fn.endsWith(".htm") && r instanceof DomainResource)
new XhtmlComposer(XhtmlComposer.HTML, true).compose(s, ((DomainResource) r).getText().getDiv());
else if (version.startsWith("3.0")) {
@ -1731,7 +1930,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
else if (fn.endsWith(".txt") || fn.endsWith(".map") )
TextFile.stringToStream(org.hl7.fhir.dstu3.utils.StructureMapUtilities.render((org.hl7.fhir.dstu3.model.StructureMap) res), s, false);
else
throw new Exception("Unsupported format for "+fn);
throw new FHIRException("Unsupported format for "+fn);
} else if (version.startsWith("4.0")) {
org.hl7.fhir.r4.model.Resource res = VersionConvertor_40_50.convertResource(r);
if (fn.endsWith(".xml") && !fn.endsWith("template.xml"))
@ -1741,7 +1940,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
else if (fn.endsWith(".txt") || fn.endsWith(".map") )
TextFile.stringToStream(org.hl7.fhir.r4.utils.StructureMapUtilities.render((org.hl7.fhir.r4.model.StructureMap) res), s, false);
else
throw new Exception("Unsupported format for "+fn);
throw new FHIRException("Unsupported format for "+fn);
} else if (version.startsWith("1.4")) {
org.hl7.fhir.dstu2016may.model.Resource res = VersionConvertor_14_50.convertResource(r);
if (fn.endsWith(".xml") && !fn.endsWith("template.xml"))
@ -1749,7 +1948,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
else if (fn.endsWith(".json") && !fn.endsWith("template.json"))
new org.hl7.fhir.dstu2016may.formats.JsonParser().setOutputStyle(org.hl7.fhir.dstu2016may.formats.IParser.OutputStyle.PRETTY).compose(s, res);
else
throw new Exception("Unsupported format for "+fn);
throw new FHIRException("Unsupported format for "+fn);
} else if (version.startsWith("1.0")) {
VersionConvertorAdvisor50 advisor = new org.hl7.fhir.convertors.misc.IGR2ConvertorAdvisor5();
org.hl7.fhir.dstu2.model.Resource res = VersionConvertor_10_50.convertResource(r, advisor);
@ -1758,7 +1957,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
else if (fn.endsWith(".json") && !fn.endsWith("template.json"))
new org.hl7.fhir.dstu2.formats.JsonParser().setOutputStyle(org.hl7.fhir.dstu2.formats.IParser.OutputStyle.PRETTY).compose(s, res);
else
throw new Exception("Unsupported format for "+fn);
throw new FHIRException("Unsupported format for "+fn);
} else if (version.equals(Constants.VERSION)) {
if (fn.endsWith(".xml") && !fn.endsWith("template.xml"))
new XmlParser().setOutputStyle(org.hl7.fhir.r5.formats.IParser.OutputStyle.PRETTY).compose(s, r);
@ -1767,9 +1966,9 @@ public class ValidationEngine implements IValidatorResourceFetcher {
else if (fn.endsWith(".txt") || fn.endsWith(".map") )
TextFile.stringToStream(org.hl7.fhir.r5.utils.StructureMapUtilities.render((org.hl7.fhir.r5.model.StructureMap) r), s, false);
else
throw new Exception("Unsupported format for "+fn);
throw new FHIRException("Unsupported format for "+fn);
} else
throw new Exception("Encounted unsupported configured version "+version+" loading "+fn);
throw new FHIRException("Encounted unsupported configured version "+version+" loading "+fn);
s.close();
}
@ -1833,7 +2032,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
} else if (VersionUtilities.isR4Ver(version)) {
return convertVersionNativeR4(targetVer, cnt, format);
} else {
throw new Exception("Source version not supported yet: "+version);
throw new FHIRException("Source version not supported yet: "+version);
}
} catch (Exception e) {
System.out.println("Conversion failed using Java convertor: "+e.getMessage());
@ -1940,7 +2139,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
throw new FHIRException("Unsupported output format: "+cnt.cntType.toString());
}
} else {
throw new Exception("Target Version not supported yet: "+targetVer);
throw new FHIRException("Target Version not supported yet: "+targetVer);
}
}
@ -2009,7 +2208,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
throw new FHIRException("Unsupported output format: "+cnt.cntType.toString());
}
} else {
throw new Exception("Target Version not supported yet: "+targetVer);
throw new FHIRException("Target Version not supported yet: "+targetVer);
}
}
@ -2078,7 +2277,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
throw new FHIRException("Unsupported output format: "+cnt.cntType.toString());
}
} else {
throw new Exception("Target Version not supported yet: "+targetVer);
throw new FHIRException("Target Version not supported yet: "+targetVer);
}
}
@ -2147,7 +2346,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
throw new FHIRException("Unsupported output format: "+cnt.cntType.toString());
}
} else {
throw new Exception("Target Version not supported yet: "+targetVer);
throw new FHIRException("Target Version not supported yet: "+targetVer);
}
}
@ -2166,4 +2365,9 @@ public class ValidationEngine implements IValidatorResourceFetcher {
}
public FilesystemPackageCacheManager getPcm() {
return pcm;
}
}

View File

@ -65,6 +65,7 @@ import org.hl7.fhir.r5.model.ImplementationGuide;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.validation.ValidationEngine.VersionSourceInformation;
import org.hl7.fhir.validation.cli.ValidatorGui;
import org.hl7.fhir.validation.cli.services.ComparisonService;
import org.hl7.fhir.validation.cli.services.ValidationService;
@ -171,6 +172,10 @@ public class Validator {
Display.printCliArgumentsAndInfo(args);
cliContext = Params.loadCliContext(args);
if (cliContext.getSv() == null) {
cliContext.setSv(determineVersion(cliContext));
}
// Comment this out because definitions filename doesn't necessarily contain version (and many not even be 14 characters long). Version gets spit out a couple of lines later after we've loaded the context
String definitions = VersionUtilities.packageForVersion(cliContext.getSv()) + "#" + VersionUtilities.getCurrentVersion(cliContext.getSv());
ValidationEngine validator = ValidationService.getValidator(cliContext, definitions);
@ -204,5 +209,27 @@ public class Validator {
}
}
}
}
public static String determineVersion(CliContext cliContext) throws Exception {
if (cliContext.getMode() != EngineMode.VALIDATION) {
return "current";
}
System.out.println("Scanning for versions (no -version parameter):");
VersionSourceInformation versions = ValidationService.scanForVersions(cliContext);
for (String s : versions.getReport()) {
System.out.println(" "+s);
}
if (versions.isEmpty()) {
System.out.println("-> Using Default version '"+VersionUtilities.CURRENT_VERSION+"'");
return "current";
}
if (versions.size() == 1) {
System.out.println("-> use version "+versions.version());
return versions.version();
}
throw new Exception("-> Multiple versions found. Specify a particular version using the -version parameter");
}
}

View File

@ -36,7 +36,7 @@ public class CliContext {
@JsonProperty("txServer")
private String txServer = "http://tx.fhir.org";
@JsonProperty("sv")
private String sv = "current";
private String sv = null;
@JsonProperty("txLog")
private String txLog = null;
@JsonProperty("mapLog")

View File

@ -0,0 +1,109 @@
package org.hl7.fhir.validation.cli.services;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Locale;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.utils.IResourceValidator.IValidatorResourceFetcher;
import org.hl7.fhir.r5.utils.IResourceValidator.ReferenceValidationPolicy;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.VersionUtilities.VersionURLInfo;
import org.hl7.fhir.utilities.cache.BasePackageCacheManager;
import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager;
import org.hl7.fhir.utilities.cache.NpmPackage;
import org.hl7.fhir.validation.cli.services.StandAloneValidatorFetcher.IPackageInstaller;
public class StandAloneValidatorFetcher implements IValidatorResourceFetcher {
public interface IPackageInstaller {
public void loadIg(String src, boolean recursive) throws IOException, FHIRException;
}
private BasePackageCacheManager pcm;
private IWorkerContext context;
private IPackageInstaller installer;
public StandAloneValidatorFetcher(FilesystemPackageCacheManager pcm, IWorkerContext context, IPackageInstaller installer) {
super();
this.pcm = pcm;
this.context = context;
this.installer = installer;
}
@Override
public Element fetch(Object appContext, String url) throws FHIRFormatError, DefinitionException, FHIRException, IOException {
throw new Error("Not done yet");
}
@Override
public ReferenceValidationPolicy validationPolicy(Object appContext, String path, String url) {
throw new Error("Not done yet");
}
@Override
public boolean resolveURL(Object appContext, String path, String url) throws IOException, FHIRException {
if (!Utilities.isAbsoluteUrl(url)) {
return false;
}
// if we've got to here, it's a reference to a FHIR URL. We're going to try to resolve it on the fly
// first possibility: it's a reference to a version specific URL http://hl7.org/fhir/X.X/...
VersionURLInfo vu = VersionUtilities.parseVersionUrl(url);
if (vu != null) {
NpmPackage pi = pcm.loadPackage(VersionUtilities.packageForVersion(vu.getVersion()), VersionUtilities.getCurrentVersion(vu.getVersion()));
return pi.hasCanonical(vu.getUrl());
}
// ok maybe it's a reference to a package we know
String base = findBaseUrl(url);
String pid = pcm.getPackageId(base);
if (url.contains("|")) {
pid = pid+"#"+url.substring(url.indexOf("|")+1);
}
if (pid != null) {
installer.loadIg(pid, false);
NpmPackage pi = pcm.loadPackage(pid);
return pi.hasCanonical(url);
}
if (!url.startsWith("http://hl7.org/fhir")) {
return true; // we don't bother with those in the standalone validator - we assume they are valid
}
// we assume it's invalid at this point
return false;
}
private String findBaseUrl(String url) {
String[] p = url.split("\\/");
for (int i = 1; i< p.length; i++) {
if (Utilities.existsInList(p[i], context.getResourceNames())) {
StringBuilder b = new StringBuilder(p[0]);
for (int j = 1; j < i; j++) {
b.append("/");
b.append(p[j]);
}
return b.toString();
}
}
return null;
}
@Override
public byte[] fetchRaw(String url) throws MalformedURLException, IOException {
throw new Error("Not done yet");
}
@Override
public void setLocale(Locale locale) {
throw new Error("Not done yet");
}
}

View File

@ -11,6 +11,7 @@ import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.ValidationEngine.VersionSourceInformation;
import org.hl7.fhir.validation.cli.model.*;
import java.io.File;
@ -52,6 +53,15 @@ public class ValidationService {
return response;
}
public static VersionSourceInformation scanForVersions(CliContext cliContext) throws Exception {
VersionSourceInformation versions = new VersionSourceInformation();
ValidationEngine ve = new ValidationEngine();
for (String src : cliContext.getIgs()) {
ve.scanForIgVersion(src, cliContext.isRecursive(), versions);
}
ve.scanForVersions(cliContext.getSources(), versions);
return versions;
}
public static void validateSources(CliContext cliContext, ValidationEngine validator, long loadStart) throws Exception {
validator.doneLoading(loadStart);
if (cliContext.getProfiles().size() > 0) {
@ -205,6 +215,7 @@ public class ValidationService {
validator.setSecurityChecks(cliContext.isSecurityChecks());
validator.setCrumbTrails(cliContext.isCrumbTrails());
validator.setShowTimes(cliContext.isShowTimes());
validator.setFetcher(new StandAloneValidatorFetcher(validator.getPcm(), validator.getContext(), validator));
TerminologyCache.setNoCaching(cliContext.isNoInternalCaching());
return validator;
}

View File

@ -151,6 +151,7 @@ import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.Utilities.DecimalStatus;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.VersionUtilities.VersionURLInfo;
import org.hl7.fhir.utilities.validation.ValidationOptions;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
@ -592,8 +593,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
throw new FHIRException(e1);
}
timeTracker.load(t);
if (e != null)
if (e != null) {
validate(appContext, errors, e, profiles);
}
return e;
}
@ -1852,15 +1854,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("#") || Utilities.isAbsoluteUrl(url), I18nConstants.TYPE_SPECIFIC_CHECKS_CANONICAL_ABSOLUTE, url);
}
// now, do we check the URI target?
if (fetcher != null) {
boolean found;
try {
found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || (url.startsWith("http://hl7.org/fhir/tools")) || fetcher.resolveURL(appContext, path, url);
} catch (IOException e1) {
found = false;
if (isCanonicalURLElement(e)) {
// for now, no validation. Need to think about authority.
} else {
// now, do we check the URI target?
if (fetcher != null) {
boolean found;
try {
found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com")) || url.contains("acme.org")) || (url.startsWith("http://hl7.org/fhir/tools")) || fetcher.resolveURL(appContext, path, url);
} catch (IOException e1) {
found = false;
}
rule(errors, IssueType.INVALID, e.line(), e.col(), path, found, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URL_RESOLVE, url);
}
rule(errors, IssueType.INVALID, e.line(), e.col(), path, found, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URL_RESOLVE, url);
}
}
if (type.equals(ID)) {
@ -2007,6 +2013,24 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// for nothing to check
}
private boolean isCanonicalURLElement(Element e) {
if (e.getProperty() == null || e.getProperty().getDefinition() == null) {
return false;
}
String path = e.getProperty().getDefinition().getBase().getPath();
if (path == null) {
return false;
}
String[] p = path.split("\\.");
if (p.length != 2) {
return false;
}
if (!"url".equals(p[1])) {
return false;
}
return Utilities.existsInList(p[0], VersionUtilities.getCanonicalResourceNames(context.getVersion()));
}
private boolean containsHtmlTags(String cnt) {
int i = cnt.indexOf("<");
while (i > -1) {
@ -2928,7 +2952,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return rr;
}
}
if (stack.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY) {
if (stack.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY || stack.getElement().getSpecial() == SpecialElement.PARAMETER) {
return null; // we don't try to resolve contained references across this boundary
}
stack = stack.getParent();
@ -2963,6 +2987,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return rr;
}
}
if (stack.getElement().getSpecial() == SpecialElement.PARAMETER && stack.getParent() != null) {
NodeStack tgt = findInParams(stack.getParent().getParent(), ref);
if (tgt != null) {
ResolvedReference rr = new ResolvedReference();
rr.setResource(tgt.getElement());
rr.setFocus(tgt.getElement());
rr.setExternal(false);
rr.setStack(tgt);
rr.getStack().qualifyPath(".ofType("+tgt.getElement().fhirType()+")");
return rr;
}
}
stack = stack.getParent();
}
// we can get here if we got called via FHIRPath conformsTo which breaks the stack continuity.
@ -2989,6 +3025,42 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return null;
}
private NodeStack findInParams(NodeStack params, String ref) {
int i = 0;
for (Element child : params.getElement().getChildren("parameter")) {
NodeStack p = params.push(child, i, child.getProperty().getDefinition(), child.getProperty().getDefinition());
if (child.hasChild("resource")) {
Element res = child.getNamedChild("resource");
if ((res.fhirType()+"/"+res.getIdBase()).equals(ref)) {
return p.push(res, -1, res.getProperty().getDefinition(), res.getProperty().getDefinition());
}
}
NodeStack pc = findInParamParts(p, child, ref);
if (pc != null) {
return pc;
}
}
return null;
}
private NodeStack findInParamParts(NodeStack pp, Element param, String ref) {
int i = 0;
for (Element child : param.getChildren("part")) {
NodeStack p = pp.push(child, i, child.getProperty().getDefinition(), child.getProperty().getDefinition());
if (child.hasChild("resource")) {
Element res = child.getNamedChild("resource");
if ((res.fhirType()+"/"+res.getIdBase()).equals(ref)) {
return p.push(res, -1, res.getProperty().getDefinition(), res.getProperty().getDefinition());
}
}
NodeStack pc = findInParamParts(p, child, ref);
if (pc != null) {
return pc;
}
}
return null;
}
private Element getEntryForSource(Element bundle, Element element) {
List<Element> entries = new ArrayList<Element>();
bundle.getNamedChildren(ENTRY, entries);
@ -3432,7 +3504,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
for (Element profile : profiles) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, profile.primitiveValue());
if (!defn.getUrl().equals(profile.primitiveValue())) {
if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", sd != null, I18nConstants.VALIDATION_VAL_PROFILE_UNKNOWN, profile.primitiveValue())) {
// is this a version specific reference?
VersionURLInfo vu = VersionUtilities.parseVersionUrl(profile.primitiveValue());
if (vu != null) {
if (!VersionUtilities.versionsCompatible(vu.getVersion(), context.getVersion())) {
hint(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", false, I18nConstants.VALIDATION_VAL_PROFILE_OTHER_VERSION, vu.getVersion());
} else if (vu.getUrl().equals(defn.getUrl())) {
hint(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", false, I18nConstants.VALIDATION_VAL_PROFILE_THIS_VERSION_OK);
} else {
StructureDefinition sdt = context.fetchResource(StructureDefinition.class, vu.getUrl());
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", false, I18nConstants.VALIDATION_VAL_PROFILE_THIS_VERSION_OTHER, sdt == null ? "null" : sdt.getType());
}
} else if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", sd != null, I18nConstants.VALIDATION_VAL_PROFILE_UNKNOWN, profile.primitiveValue())) {
signpost(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), !crumbTrails, I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_META, sd.getUrl());
stack.resetIds();
startInner(hostContext, errors, resource, element, sd, stack, false);
@ -3658,9 +3741,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (trr == null) {
rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE, resourceName);
} else if (isValidResourceType(resourceName, trr)) {
long t = System.nanoTime();
StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + resourceName);
timeTracker.sd(t);
// special case: resource wrapper is reset if we're crossing a bundle boundary, but not otherwise
ValidatorHostContext hc = null;
if (element.getSpecial() == SpecialElement.BUNDLE_ENTRY || element.getSpecial() == SpecialElement.BUNDLE_OUTCOME || element.getSpecial() == SpecialElement.PARAMETER) {
@ -3669,10 +3749,29 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else {
hc = hostContext.forContained(element);
}
trackUsage(profile, hostContext, element);
stack.resetIds();
if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE, resourceName)) {
validateResource(hc, errors, resource, element, profile, idstatus, stack);
if (trr.getProfile().size() == 1) {
long t = System.nanoTime();
StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, trr.getProfile().get(0).asStringValue());
timeTracker.sd(t);
trackUsage(profile, hostContext, element);
if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE, resourceName)) {
validateResource(hc, errors, resource, element, profile, idstatus, stack);
}
} else if (trr.getProfile().size() == 0) {
long t = System.nanoTime();
StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + resourceName);
timeTracker.sd(t);
trackUsage(profile, hostContext, element);
if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE, resourceName)) {
validateResource(hc, errors, resource, element, profile, idstatus, stack);
}
} else {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
for (CanonicalType u : trr.getProfile()) {
b.append(u.asStringValue());
}
rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES, trr.getCode(), b.toString());
}
} else {
List<String> types = new ArrayList<>();

View File

@ -107,7 +107,7 @@ public class ComparisonTests {
System.out.println("---- Set up Output ----------------------------------------------------------");
Utilities.createDirectory(Utilities.path("[tmp]", "comparison"));
FilesystemPackageCacheManager pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION);
NpmPackage npm = pcm.loadPackage("hl7.fhir.pubpack", "0.0.6");
NpmPackage npm = pcm.loadPackage("hl7.fhir.pubpack", "0.0.7");
for (String f : npm.list("other")) {
TextFile.streamToFile(npm.load("other", f), Utilities.path("[tmp]", "comparison", f));
}

View File

@ -13,7 +13,7 @@ public class ProfileComparisonTests {
// ValidationEngine ve = new ValidationEngine("hl7.fhir.r3.core#3.0.2", DEF_TX, null, FhirPublication.STU3, "3.0.2");
// ve.loadIg("hl7.fhir.us.core#1.0.1", false);
// ve.loadIg("hl7.fhir.au.base#current", false);
// ve.getContext().loadFromPackage(new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION).loadPackage("hl7.fhir.pubpack", "0.0.4"), new R5ToR5Loader(new String[] {"Binary"}), "Binary");
// ve.getContext().loadFromPackage(new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION).loadPackage("hl7.fhir.pubpack", "0.0.7"), new R5ToR5Loader(new String[] {"Binary"}), "Binary");
//
//
// String left = "http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient";

View File

@ -457,7 +457,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
@Override
public boolean resolveURL(Object appContext, String path, String url) throws IOException, FHIRException {
return !url.contains("example.org");
return !url.contains("example.org") && !url.startsWith("http://hl7.org/fhir/invalid");
}
@Override