diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java index 9e26d2305..4bcbf8466 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java @@ -773,6 +773,11 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte txCache = new TerminologyCache(lock, cachePath); } + public void clearTSCache(String url) throws Exception { + txCache.removeCS(url); + } + + @Override public List findMapsForSource(String url) throws FHIRException { synchronized (lock) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java index 1608323e2..536fd4995 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TerminologyCache.java @@ -460,6 +460,15 @@ public class TerminologyCache { public static void setNoCaching(boolean noCaching) { TerminologyCache.noCaching = noCaching; } + + public void removeCS(String url) { + synchronized (lock) { + String name = getNameForSystem(url); + if (caches.containsKey(name)) { + caches.remove(name); + } + } + } } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java index 182abbafc..7b9667d3e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java @@ -655,7 +655,9 @@ public class DataRenderer { protected void renderQuantity(XhtmlNode x, Quantity q, boolean showCodeDetails) { if (q.hasComparator()) x.addText(q.getComparator().toCode()); - x.addText(q.getValue().toString()); + if (q.hasValue()) { + x.addText(q.getValue().toString()); + } if (q.hasUnit()) x.tx(" "+q.getUnit()); else if (q.hasCode()) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java index 665bc9c45..84a9a57a6 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java @@ -17,6 +17,7 @@ import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper; 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.utilities.Utilities; import org.hl7.fhir.utilities.xhtml.XhtmlNode; @@ -24,8 +25,8 @@ public class DiagnosticReportRenderer extends ResourceRenderer { public class ObservationNode { private String ref; - private ResourceWrapper obs; - private List contained = new ArrayList(); + private ResourceWithReference obs; + private List contained; } @@ -59,7 +60,7 @@ public class DiagnosticReportRenderer extends ResourceRenderer { render(h2, getProperty(dr, "issued").value()); } - XhtmlNode tbl = x.table( "grid"); + XhtmlNode tbl = x.table("grid"); XhtmlNode tr = tbl.tr(); XhtmlNode tdl = tr.td(); XhtmlNode tdr = tr.td(); @@ -96,7 +97,7 @@ public class DiagnosticReportRenderer extends ResourceRenderer { pw = getProperty(dr, "result"); if (valued(pw)) { - List observations = fetchObservations(pw.getValues()); + List observations = fetchObservations(pw.getValues(), dr); buildObservationsTable(x, observations); } @@ -149,11 +150,25 @@ public class DiagnosticReportRenderer extends ResourceRenderer { c.tx("to do"); } - private List fetchObservations(List list) { - return new ArrayList(); + private List fetchObservations(List list, ResourceWrapper rw) throws UnsupportedEncodingException, FHIRException, IOException { + List res = new ArrayList(); + for (BaseWrapper b : list) { + if (b.has("reference")) { + ObservationNode obs = new ObservationNode(); + obs.ref = b.get("reference").primitiveValue(); + obs.obs = resolveReference(rw, obs.ref); + if (obs.obs.getResource() != null) { + PropertyWrapper t = getProperty(obs.obs.getResource(), "contained"); + if (t.hasValues()) { + obs.contained = fetchObservations(t.getValues(), rw); + } + } + res.add(obs); + } + } + return res; } - private void buildObservationsTable(XhtmlNode root, List observations) { XhtmlNode tbl = root.table( "none"); for (ObservationNode o : observations) { @@ -163,16 +178,21 @@ public class DiagnosticReportRenderer extends ResourceRenderer { private void addObservationToTable(XhtmlNode tbl, ObservationNode o, int i) { XhtmlNode tr = tbl.tr(); - if (o.obs == null) { + if (o.obs.getReference() == null) { XhtmlNode td = tr.td().colspan("6"); td.i().tx("This Observation could not be resolved"); } else { - addObservationToTable(tr, o.obs, i); - // todo: contained observations - } - for (ObservationNode c : o.contained) { - addObservationToTable(tbl, c, i+1); - } + if (o.obs.getResource() != null) { + addObservationToTable(tr, o.obs.getResource(), i); + } else { + tr.td().tx("Unable to resolve Observation: "+o.obs.getReference()); + } + if (o.contained != null) { + for (ObservationNode c : o.contained) { + addObservationToTable(tbl, c, i+1); + } + } + } } private void addObservationToTable(XhtmlNode tr, ResourceWrapper obs, int i) { @@ -180,7 +200,7 @@ public class DiagnosticReportRenderer extends ResourceRenderer { // code (+bodysite) XhtmlNode td = tr.td(); - PropertyWrapper pw = getProperty(obs, "result"); + PropertyWrapper pw = getProperty(obs, "code"); if (valued(pw)) { render(td, pw.value()); } @@ -195,12 +215,7 @@ public class DiagnosticReportRenderer extends ResourceRenderer { td = tr.td(); pw = getProperty(obs, "value[x]"); if (valued(pw)) { - if (pw.getTypeCode().equals("CodeableConcept")) - render(td, pw.value()); - else if (pw.getTypeCode().equals("string")) - render(td, pw.value()); - else - td.addText(pw.getTypeCode()+" not rendered yet"); + render(td, pw.value()); } // units diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ListRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ListRenderer.java index e8f3ef251..fe0555fd1 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ListRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ListRenderer.java @@ -53,6 +53,7 @@ public class ListRenderer extends ResourceRenderer { td.tx("Code: "+displayBase(list.get("code"))); } tr = t.tr(); + td = tr.td(); if (list.has("subject")) { td.tx("Subject: "); shortForRef(td, list.get("subject")); @@ -80,27 +81,27 @@ public class ListRenderer extends ResourceRenderer { } t = x.table("grid"); tr = t.tr().style("backgound-color: #eeeeee"); - td.b().tx("Items"); + tr.td().b().tx("Items"); if (date) { - td.tx("Date"); + tr.td().tx("Date"); } if (flag) { - td.tx("Flag"); + tr.td().tx("Flag"); } if (deleted) { - td.tx("Deleted"); + tr.td().tx("Deleted"); } for (BaseWrapper e : list.children("entry")) { tr = t.tr(); - shortForRef(td, e.get("item")); + shortForRef(tr.td(), e.get("item")); if (date) { - td.tx(e.has("date") ? e.get("date").dateTimeValue().toHumanDisplay() : ""); + tr.td().tx(e.has("date") ? e.get("date").dateTimeValue().toHumanDisplay() : ""); } if (flag) { - td.tx(e.has("flag") ? displayBase(e.get("flag")) : ""); + tr.td().tx(e.has("flag") ? displayBase(e.get("flag")) : ""); } if (deleted) { - td.tx(e.has("deleted") ? e.get("deleted").primitiveValue() : ""); + tr.td().tx(e.has("deleted") ? e.get("deleted").primitiveValue() : ""); } } return false; @@ -211,8 +212,10 @@ public class ListRenderer extends ResourceRenderer { disp = url; } x.tx(disp); - } else { + } else if (r.getResource() != null) { RendererFactory.factory(r.getResource().getName(), context).renderReference(r.getResource(), x, (Reference) ref); + } else { + RendererFactory.factory(url, context).renderReference(r.getResource(), x, (Reference) ref); } } else if (disp != null) { x.tx(disp); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java index 8fcc8539b..e0fab8780 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java @@ -21,6 +21,7 @@ import org.hl7.fhir.r5.model.Base64BinaryType; import org.hl7.fhir.r5.model.BooleanType; import org.hl7.fhir.r5.model.CodeType; import org.hl7.fhir.r5.model.CodeableConcept; +import org.hl7.fhir.r5.model.CodeableReference; import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.ContactDetail; import org.hl7.fhir.r5.model.ContactPoint; @@ -227,7 +228,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer { first = false; else if (last) x.tx(", "); - last = displayLeaf(res, v, child, x, p.getName(), showCodeDetails) || last; + last = displayLeaf(res, v, child, x, p.getName(), showCodeDetails, false) || last; } } } @@ -353,6 +354,10 @@ public class ProfileDrivenRenderer extends ResourceRenderer { } private boolean displayLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode x, String name, boolean showCodeDetails) throws FHIRException, UnsupportedEncodingException, IOException { + return displayLeaf(res, ew, defn, x, name, showCodeDetails, true); + } + + private boolean displayLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode x, String name, boolean showCodeDetails, boolean allowLinks) throws FHIRException, UnsupportedEncodingException, IOException { if (ew == null) return false; Base e = ew.getBase(); @@ -399,6 +404,14 @@ public class ProfileDrivenRenderer extends ResourceRenderer { x.addText(name); return true; } + } else if (e instanceof CodeableReference) { + if (((CodeableReference) e).hasReference()) { + Reference r = ((CodeableReference) e).getReference(); + renderReference(res, x, r, allowLinks); + } else { + renderCodeableConcept(x, ((CodeableReference) e).getConcept(), showCodeDetails); + } + return true; } else if (e instanceof CodeableConcept) { renderCodeableConcept(x, (CodeableConcept) e, showCodeDetails); return true; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java index de503ad2e..f91d88d5f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java @@ -118,9 +118,13 @@ public abstract class ResourceRenderer extends DataRenderer { } public void renderReference(ResourceWrapper rw, XhtmlNode x, Reference r) throws UnsupportedEncodingException, IOException { - XhtmlNode c = x; + renderReference(rw, x, r, true); + } + + public void renderReference(ResourceWrapper rw, XhtmlNode x, Reference r, boolean allowLinks) throws UnsupportedEncodingException, IOException { + XhtmlNode c = null; ResourceWithReference tr = null; - if (r.hasReferenceElement()) { + if (r.hasReferenceElement() && allowLinks) { tr = resolveReference(rw, r.getReference()); if (!r.getReference().startsWith("#")) { @@ -129,6 +133,8 @@ public abstract class ResourceRenderer extends DataRenderer { else c = x.ah(r.getReference()); } + } else { + c = x.span(null, null); } // what to display: if text is provided, then that. if the reference was resolved, then show the generated narrative if (r.hasDisplayElement()) { @@ -178,7 +184,7 @@ public abstract class ResourceRenderer extends DataRenderer { protected ResourceWithReference resolveReference(ResourceWrapper res, String url) { if (url == null) return null; - if (url.startsWith("#")) { + if (url.startsWith("#") && res != null) { for (ResourceWrapper r : res.getContained()) { if (r.getId().equals(url.substring(1))) return new ResourceWithReference(null, r); @@ -249,8 +255,12 @@ public abstract class ResourceRenderer extends DataRenderer { protected ResourceWrapper fetchResource(BaseWrapper subject) throws UnsupportedEncodingException, FHIRException, IOException { if (context.getResolver() == null) return null; - - String url = subject.getChildByName("reference").value().getBase().primitiveValue(); + + PropertyWrapper ref = subject.getChildByName("reference"); + if (ref == null || !ref.hasValues()) { + return null; + } + String url = ref.value().getBase().primitiveValue(); ResourceWithReference rr = context.getResolver().resolve(context, url); return rr == null ? null : rr.getResource(); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java index d53cd178d..7e301704b 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java @@ -64,6 +64,10 @@ public class DOMWrappers { public List children() { if (list == null) { children = context.getProfileUtilities().getChildList(structure, definition); + if (children.isEmpty() && type != null) { + StructureDefinition sdt = context.getWorker().fetchTypeDefinition(type); + children = context.getProfileUtilities().getChildList(sdt, sdt.getSnapshot().getElementFirstRep()); + } list = new ArrayList(); for (ElementDefinition child : children) { List elements = new ArrayList(); @@ -140,8 +144,17 @@ public class DOMWrappers { @Override public String getTypeCode() { - if (definition == null || definition.getType().size() != 1) - throw new Error("not handled"); + if (definition == null || definition.getType().size() != 1) { + if (values.size() != 1) { + throw new Error("not handled"); + } + String tn = values.get(0).getLocalName().substring(tail(definition.getPath()).replace("[x]", "").length()); + if (isPrimitive(Utilities.uncapitalize(tn))) { + return Utilities.uncapitalize(tn); + } else { + return tn; + } + } return definition.getType().get(0).getWorkingCode(); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/GraphDefinitionEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/GraphDefinitionEngine.java new file mode 100644 index 000000000..975efcf06 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/GraphDefinitionEngine.java @@ -0,0 +1,276 @@ +package org.hl7.fhir.r5.utils; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.apache.xmlbeans.xml.stream.ReferenceResolver; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.model.Base; +import org.hl7.fhir.r5.model.Bundle; +import org.hl7.fhir.r5.model.Expression; +import org.hl7.fhir.r5.model.ExpressionNode; +import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r5.model.GraphDefinition; +import org.hl7.fhir.r5.model.GraphDefinition.GraphDefinitionLinkComponent; +import org.hl7.fhir.r5.model.GraphDefinition.GraphDefinitionLinkTargetComponent; +import org.hl7.fhir.r5.model.Reference; +import org.hl7.fhir.r5.model.Resource; +import org.hl7.fhir.r5.model.StringType; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.graphql.Argument; +import org.hl7.fhir.utilities.graphql.EGraphEngine; +import org.hl7.fhir.utilities.graphql.EGraphQLException; +import org.hl7.fhir.utilities.graphql.GraphQLResponse; +import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices; +import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices.ReferenceResolution; +import org.hl7.fhir.utilities.graphql.StringValue; +import org.hl7.fhir.instance.model.api.IBaseResource; + +public class GraphDefinitionEngine { + + + private static final String TAG_NAME = "Compiled.expression"; + + private IGraphQLStorageServices services; + private IWorkerContext context; + /** + * for the host to pass context into and get back on the reference resolution interface + */ + private Object appInfo; + + /** + * the focus resource - if (there instanceof one. if (there isn"t,) there instanceof no focus + */ + private Resource start; + + /** + * The package that describes the graphQL to be executed, operation name, and variables + */ + private GraphDefinition graphDefinition; + + /** + * If the graph definition is being run to validate a grph + */ + private boolean validating; + + /** + * where the output from executing the query instanceof going to go + */ + private Bundle bundle; + + private String baseURL; + private FHIRPathEngine engine; + + public GraphDefinitionEngine(IGraphQLStorageServices services, IWorkerContext context) { + super(); + this.services = services; + this.context = context; + } + + public Object getAppInfo() { + return appInfo; + } + + public void setAppInfo(Object appInfo) { + this.appInfo = appInfo; + } + + public Resource getFocus() { + return start; + } + + public void setFocus(Resource focus) { + this.start = focus; + } + + public GraphDefinition getGraphDefinition() { + return graphDefinition; + } + + public void setGraphDefinition(GraphDefinition graphDefinition) { + this.graphDefinition = graphDefinition; + } + + public Bundle getOutput() { + return bundle; + } + + public void setOutput(Bundle bundle) { + this.bundle = bundle; + } + + public IGraphQLStorageServices getServices() { + return services; + } + + public IWorkerContext getContext() { + return context; + } + + public String getBaseURL() { + return baseURL; + } + + public void setBaseURL(String baseURL) { + this.baseURL = baseURL; + } + + public boolean isValidating() { + return validating; + } + + public void setValidating(boolean validating) { + this.validating = validating; + } + + public void execute() throws EGraphEngine, EGraphQLException, FHIRException { + assert services != null; + assert start != null; + assert bundle != null; + assert baseURL != null; + assert graphDefinition != null; + graphDefinition.checkNoModifiers("definition", "Building graph from GraphDefinition"); + + check(!start.fhirType().equals(graphDefinition.getStart()), "The Graph definition requires that the start (focus reosource) is "+graphDefinition.getStart()+", but instead found "+start.fhirType()); + + if (!isInBundle(start)) { + addToBundle(start); + } + for (GraphDefinitionLinkComponent l : graphDefinition.getLink()) { + processLink(start.fhirType(), start, l, 1); + } + } + + private void check(boolean b, String msg) { + if (!b) { + throw new FHIRException(msg); + } + } + + private boolean isInBundle(Resource resource) { + for (BundleEntryComponent be : bundle.getEntry()) { + if (be.hasResource() && be.getResource().fhirType().equals(resource.fhirType()) && be.getResource().getId().equals(resource.getId())) { + return true; + } + } + return false; + } + + private void addToBundle(Resource resource) { + BundleEntryComponent be = bundle.addEntry(); + be.setFullUrl(Utilities.pathURL(baseURL, resource.fhirType(), resource.getId())); + be.setResource(resource); + } + + private void processLink(String focusPath, Resource focus, GraphDefinitionLinkComponent link, int depth) { + if (link.hasPath()) { + processLinkPath(focusPath, focus, link, depth); + } else { + processLinkTarget(focusPath, focus, link, depth); + } + } + + private void processLinkPath(String focusPath, Resource focus, GraphDefinitionLinkComponent link, int depth) { + String path = focusPath+" -> "+link.getPath(); + check(link.hasPath(), "Path is needed at "+path); + check(!link.hasSliceName(), "SliceName is not yet supported at "+path); + + ExpressionNode node; + if (link.getPathElement().hasUserData(TAG_NAME)) { + node = (ExpressionNode) link.getPathElement().getUserData(TAG_NAME); + } else { + node = engine.parse(link.getPath()); + link.getPathElement().setUserData(TAG_NAME, node); + } + List matches = engine.evaluate(null, focus, focus, focus, node); + check(!validating || matches.size() >= (link.hasMin() ? link.getMin() : 0), "Link at path "+path+" requires at least "+link.getMin()+" matches, but only found "+matches.size()); + check(!validating || matches.size() <= (link.hasMax() ? Integer.parseInt(link.getMax()) : Integer.MAX_VALUE), "Link at path "+path+" requires at most "+link.getMax()+" matches, but found "+matches.size()); + for (Base sel : matches) { + check(sel.fhirType().equals("Reference"), "Selected node from an expression must be a Reference"); // todo: should a URL be ok? + ReferenceResolution res = services.lookup(appInfo, focus, (Reference) sel); + if (res != null) { + check(res.getTargetContext() != focus, "how to handle contained resources is not yet resolved"); // todo + for (GraphDefinitionLinkTargetComponent tl : link.getTarget()) { + if (tl.getType().equals(res.getTarget().fhirType())) { + Resource r = (Resource) res.getTarget(); + if (!isInBundle(r)) { + addToBundle(r); + for (GraphDefinitionLinkComponent l : graphDefinition.getLink()) { + processLink(focus.fhirType(), r, l, depth+1); + } + } + } + } + } + } + } + + private void processLinkTarget(String focusPath, Resource focus, GraphDefinitionLinkComponent link, int depth) { + check(link.getTarget().size() == 1, "If there is no path, there must be one and only one target at "+focusPath); + check(link.getTarget().get(0).hasType(), "If there is no path, there must be type on the target at "+focusPath); + check(link.getTarget().get(0).getParams().contains("{ref}"), "If there is no path, the target must have parameters that include a parameter using {ref} at "+focusPath); + String path = focusPath+" -> "+link.getTarget().get(0).getType()+"?"+link.getTarget().get(0).getParams(); + + List list = new ArrayList<>(); + List params = new ArrayList<>(); + parseParams(params, link.getTarget().get(0).getParams(), focus); + services.listResources(appInfo, link.getTarget().get(0).getType(), params, list); + check(!validating || (list.size() >= (link.hasMin() ? link.getMin() : 0)), "Link at path "+path+" requires at least "+link.getMin()+" matches, but only found "+list.size()); + check(!validating || (list.size() <= (link.hasMax() && !link.getMax().equals("*") ? Integer.parseInt(link.getMax()) : Integer.MAX_VALUE)), "Link at path "+path+" requires at most "+link.getMax()+" matches, but found "+list.size()); + for (IBaseResource res : list) { + Resource r = (Resource) res; + if (!isInBundle(r)) { + addToBundle(r); + // Grahame Grieve 17-06-2020: this seems wrong to me - why restart? + for (GraphDefinitionLinkComponent l : graphDefinition.getLink()) { + processLink(start.fhirType(), start, l, depth+1); + } + } + } + } + + private void parseParams(List params, String value, Resource res) { + boolean refed = false; + Map> p = splitQuery(value); + for (String n : p.keySet()) { + for (String v : p.get(n)) { + if (v.equals("{ref}")) { + refed = true; + v = res.fhirType()+'/'+res.getId(); + } + params.add(new Argument(n, new StringValue(v))); + } + } + check(refed, "no use of {ref} found"); + } + + public Map> splitQuery(String string) { + final Map> query_pairs = new LinkedHashMap>(); + final String[] pairs = string.split("&"); + for (String pair : pairs) { + final int idx = pair.indexOf("="); + final String key = idx > 0 ? decode(pair.substring(0, idx), "UTF-8") : pair; + if (!query_pairs.containsKey(key)) { + query_pairs.put(key, new LinkedList()); + } + final String value = idx > 0 && pair.length() > idx + 1 ? decode(pair.substring(idx + 1), "UTF-8") : null; + query_pairs.get(key).add(value); + } + return query_pairs; + } + + private String decode(String s, String enc) { + try { + return URLDecoder.decode(s, enc); + } catch (UnsupportedEncodingException e) { + return s; + } + } + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/IGraphDefinitionEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/IGraphDefinitionEngine.java new file mode 100644 index 000000000..9d51658dd --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/IGraphDefinitionEngine.java @@ -0,0 +1,54 @@ +package org.hl7.fhir.r5.utils; + +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + 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 + list of conditions and the following disclaimer. + * 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 + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ + + +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.model.Bundle; +import org.hl7.fhir.r5.model.GraphDefinition; +import org.hl7.fhir.utilities.graphql.EGraphEngine; +import org.hl7.fhir.utilities.graphql.EGraphQLException; +import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices; + +public interface IGraphDefinitionEngine { + + void execute() throws EGraphEngine, EGraphQLException, FHIRException; + + Bundle getOutput(); + + void setAppInfo(Object appInfo); + + void setFocus(IBaseResource focus); + + void setGraphDefinition(GraphDefinition graphDefinition); + + void setServices(IGraphQLStorageServices services); +} \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/ClientUtils.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/ClientUtils.java index fc0165fa2..9b9bc0d07 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/ClientUtils.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/ClientUtils.java @@ -96,6 +96,7 @@ public class ClientUtils { public static final String DEFAULT_CHARSET = "UTF-8"; public static final String HEADER_LOCATION = "location"; + private static boolean debugging = false; private HttpHost proxy; private int timeout = 5000; @@ -323,6 +324,9 @@ public class ClientUtils { } response = httpclient.execute(request); } catch(IOException ioe) { + if (ClientUtils.debugging ) { + ioe.printStackTrace(); + } throw new EFhirClientException("Error sending Http Request: "+ioe.getMessage(), ioe); } return response; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java index b1645617b..5bda18774 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java @@ -323,13 +323,6 @@ public class BaseValidator { * @return Returns thePass (in other words, returns true if the rule did not fail validation) */ - //todo: delete this when finished i18n - protected boolean rule(List errors, IssueType type, String path, boolean thePass, String msg) { - if (!thePass) { - addValidationMessage(errors, type, -1, -1, path, msg, IssueSeverity.ERROR, null); - } - return thePass; - } protected boolean rule(List errors, IssueType type, String path, boolean thePass, String theMessage, Object... theMessageArguments) { if (!thePass) { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java index c5525cb0d..89395d687 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java @@ -2,7 +2,6 @@ package org.hl7.fhir.validation.cli.model; import com.fasterxml.jackson.annotation.JsonProperty; import org.hl7.fhir.validation.Validator; -import org.hl7.fhir.validation.cli.utils.SnomedVersion; import java.util.*; @@ -47,7 +46,7 @@ public class CliContext { @JsonProperty("fhirpath") private String fhirpath = null; @JsonProperty("snomedCT") - private String snomedCT = SnomedVersion.INTL.getCode(); + private String snomedCT = "900000000000207008"; @JsonProperty("targetVer") private String targetVer = null; @@ -339,12 +338,18 @@ public class CliContext { return this; } - public SnomedVersion getSnomedCT() { - return SnomedVersion.getFromCode(snomedCT); - } @JsonProperty("snomedCT") public String getSnomedCTCode() { + if ("intl".equals(snomedCT)) return "900000000000207008"; + if ("us".equals(snomedCT)) return "731000124108"; + if ("uk".equals(snomedCT)) return "999000041000000102"; + if ("au".equals(snomedCT)) return "32506021000036107"; + if ("ca".equals(snomedCT)) return "20611000087101"; + if ("nl".equals(snomedCT)) return "11000146104"; + if ("se".equals(snomedCT)) return "45991000052106"; + if ("es".equals(snomedCT)) return "449081005"; + if ("dk".equals(snomedCT)) return "554471000005108"; return snomedCT; } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java index 7a6b3a840..ddfdf0401 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java @@ -99,7 +99,7 @@ public class Params { throw new Error("Specified -profile without indicating profile source"); } else { p = args[++i]; - cliContext.addProfile(args[i++]); + cliContext.addProfile(p); } if (p != null && i + 1 < args.length && args[i + 1].equals("@")) { i++; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/SnomedVersion.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/SnomedVersion.java deleted file mode 100644 index dc4f01462..000000000 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/SnomedVersion.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.hl7.fhir.validation.cli.utils; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -public enum SnomedVersion { - INTL("intl", "900000000000207008"), - US("us", "731000124108"), - UK("uk", "999000041000000102"), - AU("au", "32506021000036107"), - CA("ca", "20611000087101"), - NL("nl", "11000146104"), - SE("se", "45991000052106"), - ES("es", "449081005"), - DK("dk", "554471000005108"); - - private static final String DEFAULT_CODE = "900000000000207008"; - - private final String lang; - private final String code; - - SnomedVersion(String lang, String code) { - this.lang = lang; - this.code = code; - } - - public String getLang() { - return lang; - } - - public String getCode() { - return code; - } - - public static SnomedVersion getFromCode(String code) { - return lookup.get(code); - } - - private static final Map lookup = new HashMap<>(); - - static { - for (SnomedVersion s : SnomedVersion.values()) { - lookup.put(s.getCode(), s); - } - } -} \ No newline at end of file diff --git a/pom.xml b/pom.xml index df1e16cb9..4607a50d5 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ 5.0.0 - 1.1.19 + 1.1.20 5.6.2 3.0.0-M4 0.8.5