rendering improvements for profile extensions

This commit is contained in:
Grahame Grieve 2023-06-17 09:38:27 +10:00
parent 60fe0fd0f8
commit 2d0dcbb803
14 changed files with 684 additions and 186 deletions

View File

@ -204,9 +204,19 @@ public class ProfileUtilities extends TranslatingUtilities {
ALL_TYPES // allow any unknow profile
}
private static final List<String> INHERITED_ED_URLS = Arrays.asList(
"http://hl7.org/fhir/tools/StructureDefinition/elementdefinition-binding-style",
"http://hl7.org/fhir/tools/StructureDefinition/elementdefinition-extension-style");
private static final List<String> NON_INHERITED_ED_URLS = Arrays.asList(
"http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status",
"http://hl7.org/fhir/StructureDefinition/structuredefinition-category",
"http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm",
"http://hl7.org/fhir/StructureDefinition/structuredefinition-implements",
"http://hl7.org/fhir/StructureDefinition/structuredefinition-explicit-type-name",
"http://hl7.org/fhir/StructureDefinition/structuredefinition-security-category",
"http://hl7.org/fhir/StructureDefinition/structuredefinition-wg",
"http://hl7.org/fhir/StructureDefinition/structuredefinition-normative-version",
"http://hl7.org/fhir/tools/StructureDefinition/obligation-profile",
"http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status-reason",
ToolingExtensions.EXT_OBLIGATION);
public IWorkerContext getContext() {
return this.context;
@ -781,7 +791,7 @@ public class ProfileUtilities extends TranslatingUtilities {
slice.getFocus().setMin(count);
} else {
String msg = "The slice definition for "+slice.getFocus().getId()+" has a minimum of "+slice.getFocus().getMin()+" but the slices add up to a minimum of "+count;
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+slice.getFocus().getId(), msg, forPublication ? ValidationMessage.IssueSeverity.ERROR : ValidationMessage.IssueSeverity.INFORMATION));
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+slice.getFocus().getId(), msg, forPublication ? ValidationMessage.IssueSeverity.ERROR : ValidationMessage.IssueSeverity.INFORMATION).setIgnorableError(true));
}
}
count = slice.checkMax();
@ -801,12 +811,12 @@ public class ProfileUtilities extends TranslatingUtilities {
}
if (ed.hasSliceName() && !slices.containsKey(ed.getPath())) {
String msg = "The element "+ed.getId()+" (["+i+"]) launches straight into slicing without the slicing being set up properly first";
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), msg, ValidationMessage.IssueSeverity.ERROR));
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), msg, ValidationMessage.IssueSeverity.ERROR).setIgnorableError(true));
}
if (ed.hasSliceName() && slices.containsKey(ed.getPath())) {
if (!slices.get(ed.getPath()).count(ed, ed.getSliceName())) {
String msg = "Duplicate slice name "+ed.getSliceName()+" on "+ed.getId()+" (["+i+"])";
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), msg, ValidationMessage.IssueSeverity.ERROR));
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), msg, ValidationMessage.IssueSeverity.ERROR).setIgnorableError(true));
}
}
i++;
@ -886,7 +896,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private void copyInheritedExtensions(StructureDefinition base, StructureDefinition derived) {
for (Extension ext : base.getExtension()) {
if (Utilities.existsInList(ext.getUrl(), INHERITED_ED_URLS) && !derived.hasExtension(ext.getUrl())) {
if (!Utilities.existsInList(ext.getUrl(), NON_INHERITED_ED_URLS) && !derived.hasExtension(ext.getUrl())) {
derived.getExtension().add(ext.copy());
}
}
@ -905,7 +915,7 @@ public class ProfileUtilities extends TranslatingUtilities {
} else {
focus.getConstraint().addAll(ed.getConstraint());
for (Extension ext : ed.getExtension()) {
if (Utilities.existsInList(ext.getUrl(), INHERITED_ED_URLS) && !focus.hasExtension(ext.getUrl())) {
if (!Utilities.existsInList(ext.getUrl(), NON_INHERITED_ED_URLS) && !focus.hasExtension(ext.getUrl())) {
focus.getExtension().add(ext.copy());
}
}
@ -2159,8 +2169,14 @@ public class ProfileUtilities extends TranslatingUtilities {
}
}
// hack workaround for problem in R5 snapshots
List<Extension> elist = dest.getExtensionsByUrl(ToolingExtensions.EXT_TRANSLATABLE);
if (elist.size() == 2) {
dest.getExtension().remove(elist.get(1));
}
for (Extension ext : source.getExtension()) {
if (Utilities.existsInList(ext.getUrl(), INHERITED_ED_URLS) && !dest.hasExtension(ext.getUrl())) {
if (!Utilities.existsInList(ext.getUrl(), NON_INHERITED_ED_URLS) && !dest.hasExtension(ext.getUrl())) {
dest.getExtension().add(ext.copy());
}
}
@ -2576,14 +2592,16 @@ public class ProfileUtilities extends TranslatingUtilities {
dest.setBinding(null);
}
// finally, we copy any extensions from source to dest
for (Extension ex : derived.getExtension()) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, ex.getUrl(), derivedSrc);
if (sd == null || sd.getSnapshot() == null || sd.getSnapshot().getElementFirstRep().getMax().equals("1")) {
ToolingExtensions.removeExtension(dest, ex.getUrl());
}
dest.addExtension(ex.copy());
}
// // finally, we copy any extensions from source to dest
//no, we already did.
// for (Extension ex : derived.getExtension()) {
// !
// StructureDefinition sd = context.fetchResource(StructureDefinition.class, ex.getUrl(), derivedSrc);
// if (sd == null || sd.getSnapshot() == null || sd.getSnapshot().getElementFirstRep().getMax().equals("1")) {
// ToolingExtensions.removeExtension(dest, ex.getUrl());
// }
// dest.addExtension(ex.copy());
// }
}
if (dest.hasFixed()) {
checkTypeOk(dest, dest.getFixed().fhirType(), srcSD, "fixed");

View File

@ -227,7 +227,7 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
// new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path("[tmp]", "snapshot", tail(sd.getUrl())+".xml")), sd);
} catch (Exception e) {
if (!isSuppressDebugMessages()) {
System.out.println("Unable to generate snapshot for "+tail(sd.getUrl()) +" from "+tail(sd.getBaseDefinition())+" because "+e.getMessage());
System.out.println("Unable to generate snapshot @2 for "+tail(sd.getUrl()) +" from "+tail(sd.getBaseDefinition())+" because "+e.getMessage());
if (context.getLogger().isDebugLogging()) {
e.printStackTrace();
}
@ -288,8 +288,13 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
}
pu.generateSnapshot(sd, p, p.getUrl(), sd.getUserString("webroot"), p.getName());
for (ValidationMessage msg : msgs) {
if ((!ignoreProfileErrors && msg.getLevel() == ValidationMessage.IssueSeverity.ERROR) || msg.getLevel() == ValidationMessage.IssueSeverity.FATAL)
throw new DefinitionException(context.formatMessage(I18nConstants.PROFILE___ELEMENT__ERROR_GENERATING_SNAPSHOT_, p.getName(), p.getUrl(), msg.getLocation(), msg.getMessage()));
if ((!ignoreProfileErrors && msg.getLevel() == ValidationMessage.IssueSeverity.ERROR) || msg.getLevel() == ValidationMessage.IssueSeverity.FATAL) {
if (!msg.isIgnorableError()) {
throw new DefinitionException(context.formatMessage(I18nConstants.PROFILE___ELEMENT__ERROR_GENERATING_SNAPSHOT_, p.getName(), p.getUrl(), msg.getLocation(), msg.getMessage()));
} else {
System.err.println(msg.getMessage());
}
}
}
if (!p.hasSnapshot())
throw new FHIRException(context.formatMessage(I18nConstants.PROFILE___ERROR_GENERATING_SNAPSHOT, p.getName(), p.getUrl()));

View File

@ -508,6 +508,7 @@ public interface IWorkerContext {
* @throws Exception
*/
public <T extends Resource> T fetchResource(Class<T> class_, String uri);
public <T extends Resource> T fetchResourceRaw(Class<T> class_, String uri);
public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri) throws FHIRException;
public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri, Resource sourceOfReference) throws FHIRException;
public <T extends Resource> T fetchResource(Class<T> class_, String uri, String version);

View File

@ -548,7 +548,6 @@ public class Coding extends DataType implements IBaseCoding, ICompositeType, ICo
return base;
}
public boolean matches(Coding other) {
return other.hasCode() && this.hasCode() && other.hasSystem() && this.hasSystem() && this.getCode().equals(other.getCode()) && this.getSystem().equals(other.getSystem()) ;

View File

@ -660,6 +660,10 @@ public class Identifier extends DataType implements ICompositeType {
, period, assigner);
}
public boolean matches(Identifier other) {
return hasSystem() && hasValue() && getSystem().matches(other.getSystem()) && getValue().matches(other.getValue());
}
}

View File

@ -252,4 +252,9 @@ public abstract class PrimitiveType<T> extends DataType implements IPrimitiveTyp
public String fpValue() {
return primitiveValue();
}
public boolean matches(String other) {
return other != null && other.equals(asStringValue());
}
}

View File

@ -7080,7 +7080,9 @@ public QuestionnaireItemComponent getQuestion(String linkId) {
}
return null;
}
// end addition
}

View File

@ -489,6 +489,29 @@ The type is the Canonical URL of Resource Definition that is the type this refer
}
return this;
}
public boolean matches(Reference value) {
if (value.hasReference() || hasReference()) {
if (!(value.hasReference() && hasReference())) {
return false;
}
if (!reference.matches(value.getReference())) {
return true;
}
}
if (value.hasIdentifier() || hasIdentifier()) {
if (!(value.hasIdentifier() && hasIdentifier())) {
return false;
}
if (!identifier.matches(value.getIdentifier())) {
return true;
}
}
return false;
}
// end addition
}

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.r5.conformance;
package org.hl7.fhir.r5.renderers;
import java.io.IOException;
import java.util.ArrayList;
@ -20,10 +20,7 @@ import org.hl7.fhir.r5.model.PrimitiveType;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.UsageContext;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.renderers.CodeResolver;
import org.hl7.fhir.r5.renderers.CodeResolver.CodeResolution;
import org.hl7.fhir.r5.renderers.DataRenderer;
import org.hl7.fhir.r5.renderers.IMarkdownProcessor;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.utils.PublicationHacker;
import org.hl7.fhir.r5.utils.ToolingExtensions;
@ -79,6 +76,7 @@ public class AdditionalBindingsRenderer {
isUnchanged = isUnchanged && ((purpose==null && compare.purpose==null) || purpose.equals(compare.purpose));
isUnchanged = isUnchanged && ((valueSet==null && compare.valueSet==null) || valueSet.equals(compare.valueSet));
isUnchanged = isUnchanged && ((doco==null && compare.doco==null) || doco.equals(compare.doco));
isUnchanged = isUnchanged && ((docoShort==null && compare.docoShort==null) || docoShort.equals(compare.docoShort));
isUnchanged = isUnchanged && ((usage==null && compare.usage==null) || usage.equals(compare.usage));
return isUnchanged;
}

View File

@ -13,10 +13,13 @@ import org.hl7.fhir.r5.model.CapabilityStatement.ResourceInteractionComponent;
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.model.Extension;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
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.ToolingExtensions;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
public class CapabilityStatementRenderer extends ResourceRenderer {
@ -99,20 +102,12 @@ public class CapabilityStatementRenderer extends ResourceRenderer {
if (hasSupProf) {
profCell.br();
profCell.addText("Additional supported profiles:");
for (CanonicalType sp: r.getSupportedProfile()) {
profCell.br();
profCell.nbsp().nbsp();
profCell.ah(sp.getValue()).addText(sp.getValue());
}
renderSupportedProfiles(profCell, r);
}
}
else { //Case of only supported profiles
profCell.addText("Supported profiles:");
for (CanonicalType sp: r.getSupportedProfile()) {
profCell.br();
profCell.nbsp().nbsp();
profCell.ah(sp.getValue()).addText(sp.getValue());
}
renderSupportedProfiles(profCell, r);
}
//Show capabilities
tr.td().addText(showOp(r, TypeRestfulInteraction.READ));
@ -135,6 +130,48 @@ public class CapabilityStatementRenderer extends ResourceRenderer {
return true;
}
private void renderSupportedProfiles(XhtmlNode profCell, CapabilityStatementRestResourceComponent r) throws IOException {
for (CanonicalType sp: r.getSupportedProfile()) {
profCell.br();
profCell.nbsp().nbsp();
StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, sp.getValue());
if (sd != null) {
profCell.ah(sd.getWebPath()).addText(sd.present());
} else {
profCell.ah(sp.getValue()).addText(sp.getValue());
}
}
if (r.hasExtension(ToolingExtensions.EXT_PROFILE_MAPPING)) {
profCell.br();
profCell.b().tx("Profile Mapping");
XhtmlNode tbl = profCell.table("grid");
boolean doco = false;
for (Extension ext : r.getExtensionsByUrl(ToolingExtensions.EXT_PROFILE_MAPPING)) {
doco = doco || ext.hasExtension("documentation");
}
XhtmlNode tr = tbl.tr();
tr.th().tx("Criteria");
tr.th().tx("Profile");
if (doco) {
tr.th().tx("Criteria");
}
for (Extension ext : r.getExtensionsByUrl(ToolingExtensions.EXT_PROFILE_MAPPING)) {
tr = tbl.tr();
tr.td().code().tx(ToolingExtensions.readStringExtension(ext, "search"));
String url = ToolingExtensions.readStringExtension(ext, "profile");
StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, url);
if (sd != null) {
tr.td().code().ah(sd.getWebPath()).tx(sd.present());
} else {
tr.td().code().tx(url);
}
if (doco) {
tr.td().code().markdown(ToolingExtensions.readStringExtension(ext, "documentation"), "documentation");
}
}
}
}
public void describe(XhtmlNode x, CapabilityStatement cs) {
x.tx(display(cs));
}

View File

@ -28,6 +28,7 @@ import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.CodeSystemNavigator;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.LoincLinker;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
public class CodeSystemRenderer extends TerminologyRenderer {
@ -141,10 +142,10 @@ public class CodeSystemRenderer extends TerminologyRenderer {
private String sentenceForContent(CodeSystemContentMode mode, CodeSystem cs) {
switch (mode) {
case COMPLETE: return "This code system <param name='cs'/> defines the following code<if test='code-count != 1'>s</if>:";
case EXAMPLE: return "This code system <param name='cs'/> provides some example code<if test='code-count != 1'>s</if>:";
case FRAGMENT: return "This code system <param name='cs'/> provides a fragment that includes following code<if test='code-count != 1'>s</if>:";
case NOTPRESENT: return "This code system <param name='cs'/> defines codes, but no codes are represented here";
case COMPLETE: return context.getContext().formatMessage(I18nConstants.RND_CS_CONTENT_COMPLETE);
case EXAMPLE: return context.getContext().formatMessage(I18nConstants.RND_CS_CONTENT_EXAMPLE);
case FRAGMENT: return context.getContext().formatMessage(I18nConstants.RND_CS_CONTENT_FRAGMENT);
case NOTPRESENT: return context.getContext().formatMessage(I18nConstants.RND_CS_CONTENT_NOTPRESENT);
case SUPPLEMENT:
boolean properties = CodeSystemUtilities.hasProperties(cs);
boolean designations = CodeSystemUtilities.hasDesignations(cs);
@ -158,7 +159,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
} else {
features = "features"; // ?
}
return "This code system <param name='cs'/> defines "+features+" on the following code<if test='code-count != 1'>s</if>:";
return context.getContext().formatMessage(I18nConstants.RND_CS_CONTENT_SUPPLEMENT, features);
}
throw new FHIRException("Unknown CodeSystemContentMode mode");
}

View File

@ -0,0 +1,437 @@
package org.hl7.fhir.r5.renderers;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.conformance.profile.BindingResolution;
import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent;
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent;
import org.hl7.fhir.r5.model.ActorDefinition;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.PrimitiveType;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.UsageContext;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.renderers.CodeResolver.CodeResolution;
import org.hl7.fhir.r5.renderers.ObligationsRenderer.ObligationDetail;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.utils.PublicationHacker;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.MarkDownProcessor;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece;
import org.hl7.fhir.utilities.xhtml.NodeType;
import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.hl7.fhir.utilities.xhtml.XhtmlNodeList;
public class ObligationsRenderer {
public static class ObligationDetail {
private String code;
private List<String> elementIds = new ArrayList<>();
private String actorId;
private String doco;
private String docoShort;
private String filter;
private String filterDesc;
private List<UsageContext> usage = new ArrayList<>();
private boolean isUnchanged = false;
private boolean matched = false;
private boolean removed = false;
private ValueSet vs;
private ObligationDetail compare;
private int count = 1;
public ObligationDetail(Extension ext) {
this.code = ext.getExtensionString("code");
this.actorId = ext.getExtensionString("actorId");
this.doco = ext.getExtensionString("documentation");
this.docoShort = ext.getExtensionString("shortDoco");
this.filter = ext.getExtensionString("filter");
this.filterDesc = ext.getExtensionString("filter-desc");
for (Extension usage : ext.getExtensionsByUrl("usage")) {
this.usage.add(usage.getValueUsageContext());
}
this.isUnchanged = ext.hasUserData(ProfileUtilities.UD_DERIVATION_EQUALS);
}
private String getKey() {
// Todo: Consider extending this with content from usageContext if purpose isn't sufficiently differentiating
return code + Integer.toString(count);
}
private void incrementCount() {
count++;
}
private void setCompare(ObligationDetail match) {
compare = match;
match.matched = true;
}
private boolean alreadyMatched() {
return matched;
}
public String getDoco(boolean full) {
return full ? doco : docoShort;
}
public String getCode() {
return code;
}
public boolean unchanged() {
if (!isUnchanged)
return false;
if (compare==null)
return true;
isUnchanged = true;
isUnchanged = isUnchanged && ((code==null && compare.code==null) || code.equals(compare.code));
isUnchanged = elementIds.equals(compare.elementIds);
isUnchanged = isUnchanged && ((actorId==null && compare.actorId==null) || actorId.equals(compare.actorId));
isUnchanged = isUnchanged && ((doco==null && compare.doco==null) || doco.equals(compare.doco));
isUnchanged = isUnchanged && ((docoShort==null && compare.docoShort==null) || docoShort.equals(compare.docoShort));
isUnchanged = isUnchanged && ((filter==null && compare.filter==null) || filter.equals(compare.filter));
isUnchanged = isUnchanged && ((filterDesc==null && compare.filterDesc==null) || filterDesc.equals(compare.filterDesc));
isUnchanged = isUnchanged && ((usage==null && compare.usage==null) || usage.equals(compare.usage));
return isUnchanged;
}
public boolean hasFilter() {
return filter != null;
}
public boolean hasUsage() {
return !usage.isEmpty();
}
public String getFilterDesc() {
return filterDesc;
}
public String getFilter() {
return filter;
}
public List<UsageContext> getUsage() {
return usage;
}
public boolean hasActor() {
return actorId != null;
}
public boolean hasActor(String id) {
return id.equals(actorId);
}
}
private static String STYLE_UNCHANGED = "opacity: 0.5;";
private static String STYLE_REMOVED = STYLE_UNCHANGED + "text-decoration: line-through;";
private List<ObligationDetail> obligations = new ArrayList<>();
private String corePath;
private StructureDefinition profile;
private String path;
private RenderingContext context;
private IMarkdownProcessor md;
private CodeResolver cr;
public ObligationsRenderer(String corePath, StructureDefinition profile, String path, RenderingContext context, IMarkdownProcessor md, CodeResolver cr) {
this.corePath = corePath;
this.profile = profile;
this.path = path;
this.context = context;
this.md = md;
this.cr = cr;
}
public void seeObligations(ElementDefinition element, String id) {
seeObligations(element.getExtension(), null, false, id);
}
public void seeObligations(List<Extension> list) {
seeObligations(list, null, false, "$all");
}
public void seeObligations(List<Extension> list, List<Extension> compList, boolean compare, String id) {
HashMap<String, ObligationDetail> compBindings = new HashMap<String, ObligationDetail>();
if (compare && compList!=null) {
for (Extension ext : compList) {
ObligationDetail abr = obligationDetail(ext);
if (compBindings.containsKey(abr.getKey())) {
abr.incrementCount();
}
compBindings.put(abr.getKey(), abr);
}
}
for (Extension ext : list) {
ObligationDetail obd = obligationDetail(ext);
if (("$all".equals(id) && !obd.hasActor()) || (obd.hasActor(id))) {
if (compare && compList!=null) {
ObligationDetail match = null;
do {
match = compBindings.get(obd.getKey());
if (obd.alreadyMatched())
obd.incrementCount();
} while (match!=null && obd.alreadyMatched());
if (match!=null)
obd.setCompare(match);
obligations.add(obd);
if (obd.compare!=null)
compBindings.remove(obd.compare.getKey());
} else
obligations.add(obd);
}
}
for (ObligationDetail b: compBindings.values()) {
b.removed = true;
obligations.add(b);
}
}
protected ObligationDetail obligationDetail(Extension ext) {
ObligationDetail abr = new ObligationDetail(ext);
return abr;
}
public String render() throws IOException {
if (obligations.isEmpty()) {
return "";
} else {
XhtmlNode tbl = new XhtmlNode(NodeType.Element, "table");
tbl.attribute("class", "grid");
renderTable(tbl.getChildNodes(), true);
return new XhtmlComposer(false).compose(tbl);
}
}
public void renderTable(HierarchicalTableGenerator gen, Cell c) throws FHIRFormatError, DefinitionException, IOException {
if (obligations.isEmpty()) {
return;
} else {
Piece piece = gen.new Piece("table").attr("class", "grid");
c.getPieces().add(piece);
renderTable(piece.getChildren(), false);
}
}
public void renderList(HierarchicalTableGenerator gen, Cell c) throws FHIRFormatError, DefinitionException, IOException {
if (obligations.size() > 0) {
Piece p = gen.new Piece(null);
c.addPiece(p);
if (obligations.size() == 1) {
renderObligationLI(p.getChildren(), obligations.get(0));
} else {
XhtmlNode ul = p.getChildren().ul();
for (ObligationDetail ob : obligations) {
renderObligationLI(ul.li().getChildNodes(), ob);
}
}
}
}
private void renderObligationLI(XhtmlNodeList children, ObligationDetail ob) throws IOException {
renderCode(children, ob.getCode());
if (ob.hasFilter() || ob.hasUsage()) {
children.tx(" (");
boolean ffirst = !ob.hasFilter();
if (ob.hasFilter()) {
children.span(null, ob.getFilterDesc()).code().tx(ob.getFilter());
}
for (UsageContext uc : ob.getUsage()) {
if (ffirst) ffirst = false; else children.tx(",");
if (!uc.getCode().is("http://terminology.hl7.org/CodeSystem/usage-context-type", "jurisdiction")) {
children.tx(displayForUsage(uc.getCode()));
children.tx("=");
}
CodeResolution ccr = this.cr.resolveCode(uc.getValueCodeableConcept());
children.ah(ccr.getLink(), ccr.getHint()).tx(ccr.getDisplay());
}
children.tx(")");
}
// usage
// filter
// process
}
public void renderTable(List<XhtmlNode> children, boolean fullDoco) throws FHIRFormatError, DefinitionException, IOException {
boolean doco = false;
boolean usage = false;
boolean actor = false;
boolean filter = false;
for (ObligationDetail binding : obligations) {
actor = actor || binding.actorId!=null || (binding.compare!=null && binding.compare.actorId !=null);
doco = doco || binding.getDoco(fullDoco)!=null || (binding.compare!=null && binding.compare.getDoco(fullDoco)!=null);
usage = usage || !binding.usage.isEmpty() || (binding.compare!=null && !binding.compare.usage.isEmpty());
filter = filter || binding.filter != null || (binding.compare!=null && binding.compare.filter!=null);
}
XhtmlNode tr = new XhtmlNode(NodeType.Element, "tr");
children.add(tr);
tr.td().style("font-size: 11px").b().tx("Obligations");
if (actor) {
tr.td().style("font-size: 11px").tx("Actor");
}
if (usage) {
tr.td().style("font-size: 11px").tx("Usage");
}
if (doco) {
tr.td().style("font-size: 11px").tx("Documentation");
}
if (filter) {
tr.td().style("font-size: 11px").tx("Filter");
}
for (ObligationDetail ob : obligations) {
tr = new XhtmlNode(NodeType.Element, "tr");
if (ob.unchanged()) {
tr.style(STYLE_REMOVED);
} else if (ob.removed) {
tr.style(STYLE_REMOVED);
}
children.add(tr);
XhtmlNode code = tr.td().style("font-size: 11px");
if (ob.compare!=null && ob.code.equals(ob.compare.code))
code.style("font-color: darkgray");
renderCode(code.getChildNodes(), ob.code);
if (ob.compare!=null && ob.compare.code != null && !ob.code.equals(ob.compare.code)) {
code.br();
code = code.span(STYLE_UNCHANGED, null);
renderCode(code.getChildNodes(), ob.compare.code);
}
if (actor) {
ActorDefinition ad = context.getContext().fetchResource(ActorDefinition.class, ob.actorId);
ActorDefinition compAd = null;
if (ob.compare!=null && ob.compare.actorId!=null) {
compAd = context.getContext().fetchResource(ActorDefinition.class, ob.compare.actorId);
}
XhtmlNode actorId = tr.td().style("font-size: 11px");
if (ob.compare!=null && ob.actorId.equals(ob.compare.actorId))
actorId.style(STYLE_UNCHANGED);
if (ad != null && ad.hasWebPath()) {
actorId.ah(ad.getWebPath(), ob.actorId).tx(ad.present());
} else if (ad != null) {
actorId.span(null, ob.actorId).tx(ad.present());
}
if (ob.compare!=null && ob.compare.actorId!=null && !ob.actorId.equals(ob.compare.actorId)) {
actorId.br();
actorId = actorId.span(STYLE_REMOVED, null);
if (compAd != null) {
if (compAd.hasWebPath()) {
actorId.ah(compAd.getWebPath(), ob.compare.actorId).tx(compAd.present());
} else {
actorId.span(null, ob.compare.actorId).tx(compAd.present());
}
}
}
}
if (usage) {
if (ob.usage != null) {
boolean first = true;
XhtmlNode td = tr.td();
for (UsageContext u : ob.usage) {
if (first) first = false; else td.tx(", ");
new DataRenderer(context).render(td, u);
}
} else {
tr.td();
}
}
if (doco) {
if (ob.doco != null) {
String d = fullDoco ? md.processMarkdown("Obligation.documentation", ob.doco) : ob.docoShort;
String oldD = ob.compare==null ? null : fullDoco ? md.processMarkdown("Binding.description.compare", ob.compare.doco) : ob.compare.docoShort;
tr.td().style("font-size: 11px").innerHTML(compareHtml(d, oldD));
} else {
tr.td().style("font-size: 11px");
}
}
if (filter) {
if (ob.filter != null) {
String d = "<code>"+ob.filter+"</code>" + (fullDoco ? md.processMarkdown("Binding.description", ob.filterDesc) : "");
String oldD = ob.compare==null ? null : "<code>"+ob.compare.filter+"</code>" + (fullDoco ? md.processMarkdown("Binding.description", ob.compare.filterDesc) : "");
tr.td().style("font-size: 11px").innerHTML(compareHtml(d, oldD));
} else {
tr.td().style("font-size: 11px");
}
}
}
}
private XhtmlNode compareString(XhtmlNode node, String newS, String oldS) {
if (oldS==null)
return node.tx(newS);
if (newS.equals(oldS))
return node.style(STYLE_UNCHANGED).tx(newS);
node.tx(newS);
node.br();
return node.span(STYLE_REMOVED,null).tx(oldS);
}
private String compareHtml(String newS, String oldS) {
if (oldS==null)
return newS;
if (newS.equals(oldS))
return "<span style=\"" + STYLE_UNCHANGED + "\">" + newS + "</span>";
return newS + "<br/><span style=\"" + STYLE_REMOVED + "\">" + oldS + "</span>";
}
private void renderCode(XhtmlNodeList children, String codeExpr) {
if (codeExpr != null) {
boolean first = true;
String[] codes = codeExpr.split("\\+");
for (String code : codes) {
if (first) first = false; else children.tx(" & ");
int i = code.indexOf(":");
if (i > -1) {
String c = code.substring(0, i);
code = code.substring(i+1);
children.b().tx(c.toUpperCase());
children.tx(":");
}
CodeResolution cr = this.cr.resolveCode("http://hl7.org/fhir/tools/CodeSystem/obligation", code);
code = code.replace("will-", "").replace("can-", "");
if (cr.getLink() != null) {
children.ah(cr.getLink(), cr.getHint()).tx(code);
} else {
children.span(null, cr.getHint()).tx(code);
}
}
} else {
children.span(null, "No Obligation Code?").tx("??");
}
}
public boolean hasObligations() {
return !obligations.isEmpty();
}
private String displayForUsage(Coding c) {
if (c.hasDisplay()) {
return c.getDisplay();
}
if ("http://terminology.hl7.org/CodeSystem/usage-context-type".equals(c.getSystem())) {
return c.getCode();
}
return c.getCode();
}
}

View File

@ -11,7 +11,6 @@ import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.conformance.AdditionalBindingsRenderer;
import org.hl7.fhir.r5.conformance.profile.BindingResolution;
import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
@ -59,7 +58,7 @@ import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.UsageContext;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
import org.hl7.fhir.r5.renderers.CodeResolver.CodeResolution;
import org.hl7.fhir.r5.renderers.StructureDefinitionRenderer.ObligationWrapper;
import org.hl7.fhir.r5.renderers.ObligationsRenderer.ObligationDetail;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType;
@ -86,68 +85,68 @@ import org.hl7.fhir.utilities.xhtml.XhtmlNodeList;
public class StructureDefinitionRenderer extends ResourceRenderer {
public class ObligationWrapper {
private Extension ext;
public ObligationWrapper(Extension ext) {
this.ext = ext;
}
public boolean hasActor() {
return ext.hasExtension("actor");
}
public boolean hasActor(String id) {
return ext.hasExtension("actor") && id.equals(ext.getExtensionByUrl("actor").getValue().primitiveValue());
}
public Coding getCode() {
Extension code = ext.getExtensionByUrl("code");
if (code != null && code.hasValueCoding()) {
return code.getValueCoding();
}
if (code != null && code.hasValueCodeType()) {
return new Coding().setSystem("http://hl7.org/fhir/tools/CodeSystem/obligation").setCode(code.getValueCodeType().primitiveValue());
}
return null;
}
public boolean hasFilter() {
return ext.hasExtension("filter");
}
public String getFilter() {
Extension code = ext.getExtensionByUrl("filter");
if (code != null && code.getValue() != null) {
return code.getValue().primitiveValue();
}
return null;
}
public boolean hasUsage() {
return ext.hasExtension("usage");
}
public String getFilterDocumentation() {
Extension code = ext.getExtensionByUrl("filter-desc");
if (code != null && code.getValue() != null) {
return code.getValue().primitiveValue();
}
return null;
}
public List<UsageContext> getUsage() {
List<UsageContext> usage = new ArrayList<>();
for (Extension u : ext.getExtensionsByUrl("usage" )) {
if (u.hasValueUsageContext()) {
usage.add(u.getValueUsageContext());
}
}
return usage;
}
}
// public class ObligationWrapper {
//
// private Extension ext;
//
// public ObligationWrapper(Extension ext) {
// this.ext = ext;
// }
//
// public boolean hasActor() {
// return ext.hasExtension("actor");
// }
//
// public boolean hasActor(String id) {
// return ext.hasExtension("actor") && id.equals(ext.getExtensionByUrl("actor").getValue().primitiveValue());
// }
//
// public Coding getCode() {
// Extension code = ext.getExtensionByUrl("obligation");
// if (code != null && code.hasValueCoding()) {
// return code.getValueCoding();
// }
// if (code != null && code.hasValueCodeType()) {
// return new Coding().setSystem("http://hl7.org/fhir/tools/CodeSystem/obligation").setCode(code.getValueCodeType().primitiveValue());
// }
// return null;
// }
//
// public boolean hasFilter() {
// return ext.hasExtension("filter");
// }
//
// public String getFilter() {
// Extension code = ext.getExtensionByUrl("filter");
// if (code != null && code.getValue() != null) {
// return code.getValue().primitiveValue();
// }
// return null;
// }
//
// public boolean hasUsage() {
// return ext.hasExtension("usage");
// }
//
// public String getFilterDocumentation() {
// Extension code = ext.getExtensionByUrl("filter-desc");
// if (code != null && code.getValue() != null) {
// return code.getValue().primitiveValue();
// }
// return null;
// }
//
// public List<UsageContext> getUsage() {
// List<UsageContext> usage = new ArrayList<>();
// for (Extension u : ext.getExtensionsByUrl("usage" )) {
// if (u.hasValueUsageContext()) {
// usage.add(u.getValueUsageContext());
// }
// }
// return usage;
// }
//
// }
@ -621,7 +620,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
genElementBindings(gen, element, columns, row, profile, corePath);
break;
case OBLIGATIONS:
genElementObligations(gen, element, columns, row);
genElementObligations(gen, element, columns, row, corePath, profile);
break;
case SUMMARY:
genElementCells(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, nc, mustSupport, true, rc);
@ -729,91 +728,16 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
return slicingRow;
}
private void genElementObligations(HierarchicalTableGenerator gen, ElementDefinition element, List<Column> columns, Row row) throws IOException {
private void genElementObligations(HierarchicalTableGenerator gen, ElementDefinition element, List<Column> columns, Row row, String corePath, StructureDefinition profile) throws IOException {
for (Column col : columns) {
Cell gc = gen.new Cell();
row.getCells().add(gc);
List<ObligationWrapper> obligations = collectObligations(element, col.id);
if (obligations.size() > 0) {
Piece p = gen.new Piece(null);
gc.addPiece(p);
if (obligations.size() == 1) {
renderObligation(p.getChildren(), obligations.get(0));
} else {
XhtmlNode ul = p.getChildren().ul();
for (ObligationWrapper ob : obligations) {
renderObligation(ul.li().getChildNodes(), ob);
}
}
}
ObligationsRenderer obr = new ObligationsRenderer(corePath, profile, element.getPath(), context, null, this);
obr.seeObligations(element, col.id);
obr.renderList(gen, gc);
}
}
private List<ObligationWrapper> collectObligations(ElementDefinition element, String id) {
List<ObligationWrapper> res = new ArrayList<>();
for (Extension ext : element.getExtensionsByUrl("http://hl7.org/fhir/tools/StructureDefinition/obligation")) {
ObligationWrapper ob = new ObligationWrapper(ext);
if (("$all".equals(id) && !ob.hasActor()) || (ob.hasActor(id))) {
res.add(ob);
}
}
return res;
}
private void renderObligation(XhtmlNodeList children, ObligationWrapper ob) throws IOException {
if ("http://hl7.org/fhir/tools/CodeSystem/obligation".equals(ob.getCode().getSystem())) {
boolean first = true;
String[] codes = ob.getCode().getCode().split("\\+");
for (String code : codes) {
if (first) first = false; else children.tx(" & ");
int i = code.indexOf(":");
if (i > -1) {
String c = code.substring(0, i);
code = code.substring(i+1);
children.b().tx(c.toUpperCase());
children.tx(":");
}
CodeResolution cr = resolveCode("http://hl7.org/fhir/tools/CodeSystem/obligation", code);
code = code.replace("will-", "").replace("can-", "");
if (cr.getLink() != null) {
children.ah(cr.getLink(), cr.getHint()).tx(code);
} else {
children.span(null, cr.getHint()).tx(code);
}
}
} else {
CodeResolution cr = resolveCode(ob.getCode());
if (cr.getLink() != null) {
children.ah(cr.getLink(), cr.getHint()).tx(cr.getDisplay());
} else {
children.span(null, cr.getHint()).tx(cr.getDisplay());
}
}
if (ob.hasFilter() || ob.hasUsage()) {
children.tx(" (");
boolean ffirst = !ob.hasFilter();
if (ob.hasFilter()) {
children.span(null, ob.getFilterDocumentation()).code().tx(ob.getFilter());
}
for (UsageContext uc : ob.getUsage()) {
if (ffirst) ffirst = false; else children.tx(",");
if (!uc.getCode().is("http://terminology.hl7.org/CodeSystem/usage-context-type", "jurisdiction")) {
children.tx(displayForUsage(uc.getCode()));
children.tx("=");
}
CodeResolution ccr = resolveCode(uc.getValueCodeableConcept());
children.ah(ccr.getLink(), ccr.getHint()).tx(ccr.getDisplay());
}
children.tx(")");
}
// usage
// filter
// process
}
private String displayForUsage(Coding c) {
if (c.hasDisplay()) {
return c.getDisplay();
@ -1229,7 +1153,6 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
c.getPieces().add(gen.new Piece(null, "This type can be bound to a value set using the ", null));
c.getPieces().add(gen.new Piece(null, ToolingExtensions.readStringExtension(profile, ToolingExtensions.EXT_BINDING_STYLE), null));
c.getPieces().add(gen.new Piece(null, " binding style", null));
}
if (definition.hasExtension(ToolingExtensions.EXT_IMPLIED_PREFIX)) {
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
@ -1251,6 +1174,13 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
}
}
}
if (definition.hasExtension(ToolingExtensions.EXT_DATE_FORMAT)) {
String df = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_DATE_FORMAT);
if (df != null) {
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
c.getPieces().add(gen.new Piece(null, "Date Format: "+df, null));
}
}
if (definition.hasExtension(ToolingExtensions.EXT_ID_EXPECTATION)) {
String ide = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_ID_EXPECTATION);
if (ide.equals("optional")) {
@ -1314,7 +1244,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
}
if (definition.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) {
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
String code = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_EMPTY);
String code = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_PROP_KEY);
c.getPieces().add(gen.new Piece(null, "JSON: Represented as a single JSON Object with named properties using the value of the "+code+" child as the key", null));
}
if (definition.hasExtension(ToolingExtensions.EXT_TYPE_SPEC)) {
@ -1337,6 +1267,34 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
}
}
}
if (root) {
if (ToolingExtensions.readBoolExtension(profile, ToolingExtensions.EXT_OBLIGATION_PROFILE_FLAG)) {
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
c.addPiece(gen.new Piece(null, "This is an obligation profile that only contains obligations and additional bindings", null).addStyle("font-weight:bold"));
}
for (Extension ext : profile.getExtensionsByUrl(ToolingExtensions.EXT_OBLIGATION_INHERITS)) {
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
String iu = ext.getValue().primitiveValue();
c.addPiece(gen.new Piece(null, "This profile picks up obligations and additional bindings from ", null).addStyle("font-weight:bold"));
StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, iu);
if (sd == null) {
c.addPiece(gen.new Piece(null, iu, null).addStyle("font-weight:bold"));
} else if (sd.hasWebPath()) {
c.addPiece(gen.new Piece(sd.getWebPath(), sd.present(), null).addStyle("font-weight:bold"));
} else {
c.addPiece(gen.new Piece(iu, sd.present(), null).addStyle("font-weight:bold"));
}
}
if (profile.getKind() == StructureDefinitionKind.LOGICAL) {
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
if (ToolingExtensions.readBoolExtension(profile, ToolingExtensions.EXT_LOGICAL_TARGET)) {
c.addPiece(gen.new Piece(null, "This logical model can be the target of a reference", null).addStyle("font-weight:bold"));
} else {
c.addPiece(gen.new Piece(null, "This logical model cannot be the target of a reference", null).addStyle("font-weight:bold"));
}
}
}
if (definition != null) {
ElementDefinitionBindingComponent binding = null;
if (valueDefn != null && valueDefn.hasBinding() && !valueDefn.getBinding().isEmpty())
@ -1422,6 +1380,13 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, buildJson(ex.getValue()), null).addStyle("color: darkgreen")));
}
}
ObligationsRenderer obr = new ObligationsRenderer(corePath, profile, definition.getPath(), rc, null, this);
if (definition.hasExtension(ToolingExtensions.EXT_OBLIGATION)) {
obr.seeObligations(definition.getExtensionsByUrl(ToolingExtensions.EXT_OBLIGATION));
}
obr.renderTable(gen, c);
if (definition.hasMaxLength() && definition.getMaxLength()!=0) {
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, "Max Length: ", null).addStyle("font-weight:bold")));

View File

@ -139,6 +139,8 @@ public class ToolingExtensions {
public static final String EXT_JSON_NAME = "http://hl7.org/fhir/tools/StructureDefinition/elementdefinition-json-name";
public static final String EXT_BINDING_STYLE = "http://hl7.org/fhir/tools/StructureDefinition/elementdefinition-binding-style";
public static final String EXT_EXTENSION_STYLE = "http://hl7.org/fhir/tools/StructureDefinition/elementdefinition-extension-style";
public static final String EXT_LOGICAL_TARGET = "http://hl7.org/fhir/tools/StructureDefinition/logical-target";
public static final String EXT_PROFILE_MAPPING = "http://hl7.org/fhir/tools/StructureDefinition/profile-mapping";
// validated
// private static final String EXT_OID = "http://hl7.org/fhir/StructureDefinition/valueset-oid";
@ -239,6 +241,7 @@ public class ToolingExtensions {
public static final String EXT_MAPPING_TGTCARD = "http://hl7.org/fhir/tools/StructureDefinition/conceptmap-target-cardinality";
public static final String WEB_EXTENSION_STYLE = "http://build.fhir.org/ig/FHIR/fhir-tools-ig/format-extensions.html#extension-related-extensions";
public static final String WEB_BINDING_STYLE = "http://build.fhir.org/ig/FHIR/fhir-tools-ig/StructureDefinition-binding-style.html";
public static final String EXT_IGDEP_COMMENT = "http://hl7.org/fhir/tools/StructureDefinition/implementationguide-dependency-comment";
public static final String EXT_XPATH_CONSTRAINT = "http://hl7.org/fhir/4.0/StructureDefinition/extension-ElementDefinition.constraint.xpath";
public static final String EXT_OBLIGATION = "http://hl7.org/fhir/tools/StructureDefinition/obligation";