update ResourceElement + tests

This commit is contained in:
Grahame Grieve 2024-06-11 21:35:11 +10:00
parent 093758429a
commit 4a73b746bd
5 changed files with 328 additions and 9 deletions

View File

@ -453,5 +453,16 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
return false;
}
public boolean isDomainResource(String typeName) {
StructureDefinition sd = context.fetchTypeDefinition(typeName);
while (sd != null) {
if ("DomainResource".equals(sd.getType())) {
return true;
}
sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
}
return false;
}
}

View File

@ -30,9 +30,19 @@ import org.hl7.fhir.utilities.xml.XmlGenerator;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* This class is only used in kindling, and it's going to be phased out and replaced by
* ElementWrappers. Don't use in any other context
*/
@Deprecated
public class DOMWrappers {
/**
* This class is only used in kindling, and it's going to be phased out and replaced by
* ElementWrappers. Don't use in any other context
*/
@Deprecated
public static class BaseWrapperElement extends WrapperBaseImpl implements BaseWrapper {
private Element element;
private String type;
@ -117,6 +127,11 @@ public class DOMWrappers {
}
/**
* This class is only used in kindling, and it's going to be phased out and replaced by
* ElementWrappers. Don't use in any other context
*/
@Deprecated
public static class PropertyWrapperElement extends RendererWrapperImpl implements PropertyWrapper {
private StructureDefinition structure;
@ -237,6 +252,11 @@ public class DOMWrappers {
}
/**
* This class is only used in kindling, and it's going to be phased out and replaced by
* ElementWrappers. Don't use in any other context
*/
@Deprecated
public static class ResourceWrapperElement extends WrapperBaseImpl implements ResourceWrapper {
private Element wrapped;
@ -422,8 +442,6 @@ public class DOMWrappers {
public Resource getResource() {
return null;
}
}
}

View File

@ -16,6 +16,7 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
import org.hl7.fhir.r5.context.ContextUtilities;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.fhirpath.FHIRPathEngine.IEvaluationContext;
@ -245,6 +246,7 @@ public class RenderingContext extends RenderingI18nContext {
private List<String> codeSystemPropList = new ArrayList<>();
private ProfileUtilities profileUtilitiesR;
private ContextUtilities contextUtilities;
private String definitionsTarget;
private String destDir;
private boolean inlineGraphics;
@ -308,6 +310,7 @@ public class RenderingContext extends RenderingI18nContext {
res.codeSystemPropList.addAll(codeSystemPropList);
res.profileUtilitiesR = profileUtilitiesR;
res.contextUtilities = contextUtilities;
res.definitionsTarget = definitionsTarget;
res.destDir = destDir;
res.addGeneratedNarrativeHeader = addGeneratedNarrativeHeader;
@ -797,6 +800,21 @@ public class RenderingContext extends RenderingI18nContext {
return t.asStringValue();
}
public String getTranslated(ResourceElement t) {
if (locale != null) {
for (ResourceElement e : t.extensions(ToolingExtensions.EXT_TRANSLATION)) {
String l = e.extensionString("lang");
if (l != null && l.equals(locale.toString())) {
String v = e.extensionString("content");
if (v != null) {
return v;
}
}
}
}
return t.primitiveValue();
}
public StringType getTranslatedElement(PrimitiveType<?> t) {
if (locale != null) {
StringType v = ToolingExtensions.getLanguageTranslationElement(t, locale.toString());
@ -843,6 +861,21 @@ public class RenderingContext extends RenderingI18nContext {
}
}
public String getTranslatedCode(String code, String codeSystem) {
if (locale != null) {
try {
ValidationResult t = getContext().validateCode(getTerminologyServiceOptions().withLanguage(locale.toString()).withVersionFlexible(true), codeSystem, null, code, null);
if (t.isOk() && t.getDisplay() != null) {
return t.getDisplay();
}
} catch (Exception ex) {
// nothing
}
}
return code;
}
public String getTranslatedCode(Enumeration<?> e, String codeSystem) {
if (locale != null) {
String v = ToolingExtensions.getLanguageTranslation(e, locale.toString());
@ -912,5 +945,12 @@ public class RenderingContext extends RenderingI18nContext {
setLocale(new Locale(locale));
return this;
}
public ContextUtilities getContextUtilities() {
if (contextUtilities == null) {
contextUtilities = new ContextUtilities(worker);
}
return contextUtilities;
}
}

View File

@ -1,14 +1,23 @@
package org.hl7.fhir.r5.renderers.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.function.BooleanSupplier;
import org.hl7.fhir.r5.context.ContextUtilities;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.DataType;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Narrative;
import org.hl7.fhir.r5.model.Property;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.utils.client.network.FhirRequestBuilder;
import org.hl7.fhir.utilities.xhtml.NodeType;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
/**
* This class is used to walk through the resources when rendering, whether
@ -46,6 +55,15 @@ public class ResourceElement {
this.element = resource;
}
public ResourceElement(ContextUtilities context, DataType type) {
this.context = context;
this.parent = null;
this.name = null;
this.index = -1;
this.kind = null;
this.element = type;
}
public ResourceElement(ContextUtilities context, ResourceElement parent, String name, int index, ElementKind kind, Base element) {
this.context = context;
this.parent = parent;
@ -73,6 +91,13 @@ public class ResourceElement {
this.model = em;
}
public String fhirVersion() {
if (element != null) {
return element.getFHIRPublicationVersion().toCode();
} else {
return model.getFHIRPublicationVersion().toCode();
}
}
public String path() {
if (parent == null) {
return fhirType();
@ -150,6 +175,16 @@ public class ResourceElement {
return child == null ? null : child.primitiveValue();
}
public String primitiveValueMN(String... names) {
ResourceElement child = childMN(names);
return child == null ? null : child.primitiveValue();
}
public String firstPrimitiveValue(String name) {
ResourceElement child = firstChild(name);
return child == null ? null : child.primitiveValue();
}
private void loadChildren() {
if (children == null) {
children = new ArrayList<>();
@ -248,6 +283,24 @@ public class ResourceElement {
return list;
}
/**
* For when an item has been renamed - find by any of the names
* @param name
* @return
*/
public List<ResourceElement> childrenMN(String... names) {
loadChildren();
List<ResourceElement> list = new ArrayList<ResourceElement>();
for (ResourceElement e : children) {
for (String name : names) {
if (name.equals(e.name())) {
list.add(e);
}
}
}
return list;
}
public ResourceElement child(String name) {
loadChildren();
@ -265,6 +318,30 @@ public class ResourceElement {
return res;
}
/**
* For when an item has been renamed - find by any of the names
* @param names
* @return
*/
public ResourceElement childMN(String... names) {
loadChildren();
ResourceElement res = null;
for (ResourceElement e : children) {
for (String name : names) {
if (name.equals(e.name()) || (name+"[x]").equals(e.name())) {
if (res == null) {
res = e;
} else {
throw new Error("Duplicated element '"+name+"' @ '"+path()+"'");
}
}
}
}
return res;
}
public boolean has(String name) {
for (ResourceElement e : children) {
if (name.equals(e.name())) {
@ -346,6 +423,17 @@ public class ResourceElement {
return res;
}
public List<ResourceElement> extensions() {
List<ResourceElement> res = new ArrayList<ResourceElement>();
loadChildren();
for (ResourceElement e : children) {
if ("Extension".equals(e.fhirType())) {
res.add(e);
}
}
return res;
}
public List<ResourceElement> extensionValues(String url) {
List<ResourceElement> res = new ArrayList<ResourceElement>();
loadChildren();
@ -359,5 +447,159 @@ public class ResourceElement {
return res;
}
public boolean canHaveNarrative() {
if (!isResource()) {
return false;
}
if (element != null) {
return element instanceof DomainResource;
} else {
return context.isDomainResource(fhirType());
}
}
public XhtmlNode getNarrative() {
if (!canHaveNarrative()) {
return null;
}
ResourceElement text = child("text");
if (text == null) {
return null;
}
ResourceElement div = text.child("div");
if (div == null) {
return null;
}
if (div.element != null) {
return div.element.getXhtml();
} else {
return div.model.getXhtml();
}
}
public boolean hasNarrative() {
if (!canHaveNarrative()) {
return false;
}
ResourceElement text = child("text");
if (text == null) {
return false;
}
ResourceElement div = text.child("div");
if (div == null) {
return false;
}
if (div.element != null) {
return div.element.getXhtml() != null;
} else {
return div.model.getXhtml() != null;
}
}
public void setNarrative(XhtmlNode x, String status, boolean multiLangMode, Locale locale) {
if (element != null) {
if (element instanceof DomainResource) {
DomainResource r = (DomainResource) element;
r.getText().setUserData("renderer.generated", true);
if (!r.hasText() || !r.getText().hasDiv()) {
r.setText(new Narrative());
r.getText().setStatusAsString(status);
}
if (multiLangMode) {
if (!r.getText().hasDiv()) {
XhtmlNode div = new XhtmlNode(NodeType.Element, "div");
div.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
r.getText().setDiv(div);
} else {
r.getText().getDiv().getChildNodes().removeIf(c -> !"div".equals(c.getName()) || !c.hasAttribute("xml:lang"));
}
markLanguage(x, locale);
r.getText().getDiv().getChildNodes().add(x);
} else {
if (!x.hasAttribute("xmlns"))
x.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
if (r.hasLanguage()) {
// use both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues
x.setAttribute("lang", r.getLanguage());
x.setAttribute("xml:lang", r.getLanguage());
}
r.getText().setDiv(x);
}
} else {
throw new Error("Cannot call setNarrative on a "+element.fhirType());
}
}
}
public void markLanguage(XhtmlNode x, Locale locale) {
x.setAttribute("lang", locale.toString());
x.setAttribute("xml:lang", locale.toString());
x.addTag(0, "hr");
x.addTag(0, "p").b().tx(locale.getDisplayName());
x.addTag(0, "hr");
}
public String getId() {
if (element != null) {
return element.getIdBase();
} else {
return model.getIdBase();
}
}
@Override
public String toString() {
return name + (index == -1 ? "" : "["+index+"]")+": "+fhirType()+" ("+kind+")";
}
public boolean matches(ResourceElement b) {
}
public String extensionString(String url) {
ResourceElement re = extensionValue(url);
return re == null ? null : re.primitiveValue();
}
public boolean isEmpty() {
if (hasChildren()) {
for (ResourceElement c : children) {
if (!c.isEmpty()) {
return false;
}
}
}
return !isPrimitive() || !hasPrimitiveValue();
}
public Resource getResource() {
return element == null ? null : (Resource) element;
}
public ResourceElement firstChild(String name) {
List<ResourceElement> list = children(name);
return list.size() == 0 ? null : list.get(0);
}
public ContextUtilities getContextUtilities() {
return context;
}
public boolean hasFormatComment() {
if (element != null) {
return element.hasFormatComment();
} else {
return model.hasFormatComment();
}
}
public Collection<String> getFormatCommentsPre() {
if (element != null) {
return element.getFormatCommentsPre();
} else {
return model.getFormatCommentsPre();
}
}
}

View File

@ -41,7 +41,11 @@ public class ResourceElementTests {
private void checkTree(ResourceElement bnd) {
Assertions.assertTrue(bnd.fhirType().equals("Bundle"));
Assertions.assertNull(bnd.name());
Assertions.assertNull(bnd.getId());
Assertions.assertEquals("Bundle", bnd.path());
Assertions.assertEquals("5.0.0", bnd.fhirVersion());
Assertions.assertFalse(bnd.canHaveNarrative());
Assertions.assertFalse(bnd.hasNarrative());
Assertions.assertEquals(ElementKind.IndependentResource, bnd.kind());
ResourceElement type = bnd.child("type");
@ -115,20 +119,24 @@ public class ResourceElementTests {
private void checkObservation(ResourceElement obs) {
Assertions.assertTrue(obs.fhirType().equals("Observation"));
Assertions.assertEquals("resource", obs.name());
Assertions.assertEquals("obs1", obs.getId());
Assertions.assertEquals("Bundle.entry[0].resource", obs.path());
Assertions.assertEquals(ElementKind.BundleEntry, obs.kind());
Assertions.assertTrue(obs.canHaveNarrative());
Assertions.assertTrue(obs.hasNarrative());
Assertions.assertNotNull(obs.getNarrative());
List<ResourceElement> children = obs.children();
assertEquals(3, children.size());
assertEquals(5, children.size());
checkObsCode(children.get(1));
checkObsCode(children.get(3));
assertEquals(children.get(2), obs.child("value"));
assertEquals(children.get(2), obs.child("value[x]"));
checkObsValue(children.get(2));
assertEquals(children.get(4), obs.child("value"));
assertEquals(children.get(4), obs.child("value[x]"));
checkObsValue(children.get(4));
assertEquals(children.get(0), obs.child("contained"));
checkContained(children.get(0));
assertEquals(children.get(2), obs.child("contained"));
checkContained(children.get(2));
}
private void checkContained(ResourceElement cont) {