Merge pull request #932 from lmckenzi/IGRenderingChanges

Ig rendering changes
This commit is contained in:
Grahame Grieve 2022-10-07 08:05:28 +11:00 committed by GitHub
commit d820eea278
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 225 additions and 74 deletions

View File

@ -2,6 +2,7 @@ package org.hl7.fhir.r5.conformance;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.DefinitionException;
@ -32,10 +33,46 @@ public class AdditionalBindingsRenderer {
private String doco; private String doco;
private String docoShort; private String docoShort;
private UsageContext usage; private UsageContext usage;
private boolean any; private boolean any = false;
private boolean unchanged; private boolean isUnchanged = false;
private boolean matched = false;
private boolean removed = false;
private AdditionalBindingDetail compare;
private int count = 1;
private String getKey() {
// Todo: Consider extending this with content from usageContext if purpose isn't sufficiently differentiating
return purpose + Integer.toString(count);
}
private void incrementCount() {
count++;
}
private void setCompare(AdditionalBindingDetail match) {
compare = match;
match.matched = true;
}
private boolean alreadyMatched() {
return matched;
}
public String getDoco(boolean full) {
return full ? doco : docoShort;
}
public boolean unchanged() {
if (!isUnchanged)
return false;
if (compare==null)
return true;
isUnchanged = true;
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 && ((usage==null && compare.usage==null) || usage.equals(compare.usage));
return isUnchanged;
}
} }
private static String STYLE_UNCHANGED = "opacity: 0.5;";
private static String STYLE_REMOVED = STYLE_UNCHANGED + "text-decoration: line-through;";
private List<AdditionalBindingDetail> bindings = new ArrayList<>(); private List<AdditionalBindingDetail> bindings = new ArrayList<>();
private ProfileKnowledgeProvider pkp; private ProfileKnowledgeProvider pkp;
private String corePath; private String corePath;
@ -53,35 +90,87 @@ public class AdditionalBindingsRenderer {
this.md = md; this.md = md;
} }
public void seeMaxBinding(Extension ext) { public void seeMaxBinding(Extension ext) {
seeMaxBinding(ext, null, false);
}
public void seeMaxBinding(Extension ext, Extension compExt, boolean compare) {
seeBinding(ext, compExt, compare, "maximum");
}
protected void seeBinding(Extension ext, Extension compExt, boolean compare, String label) {
AdditionalBindingDetail abr = new AdditionalBindingDetail(); AdditionalBindingDetail abr = new AdditionalBindingDetail();
abr.purpose = "maximum"; abr.purpose = label;
abr.valueSet = ext.getValue().primitiveValue(); abr.valueSet = ext.getValue().primitiveValue();
abr.unchanged = ext.hasUserData(ProfileUtilities.DERIVATION_EQUALS); if (compare) {
bindings.add(abr); abr.isUnchanged = compExt!=null && ext.getValue().primitiveValue().equals(compExt.getValue().primitiveValue());
abr.compare = new AdditionalBindingDetail();
abr.compare.valueSet = compExt==null ? null : compExt.getValue().primitiveValue();
} else {
abr.isUnchanged = ext.hasUserData(ProfileUtilities.DERIVATION_EQUALS);
}
bindings.add(abr);
} }
public void seeMinBinding(Extension ext) { public void seeMinBinding(Extension ext) {
AdditionalBindingDetail abr = new AdditionalBindingDetail(); seeMinBinding(ext, null, false);
abr.purpose = "minimum"; }
abr.valueSet = ext.getValue().primitiveValue();
abr.unchanged = ext.hasUserData(ProfileUtilities.DERIVATION_EQUALS); public void seeMinBinding(Extension ext, Extension compExt, boolean compare) {
bindings.add(abr); seeBinding(ext, compExt, compare, "minimum");
} }
public void seeAdditionalBindings(List<Extension> list) { public void seeAdditionalBindings(List<Extension> list) {
for (Extension ext : list) { seeAdditionalBindings(list, null, false);
AdditionalBindingDetail abr = new AdditionalBindingDetail(); }
abr.purpose = ext.getExtensionString("purpose");
abr.valueSet = ext.getExtensionString("valueSet"); public void seeAdditionalBindings(List<Extension> list, List<Extension> compList, boolean compare) {
abr.doco = ext.getExtensionString("documentation"); HashMap<String, AdditionalBindingDetail> compBindings = new HashMap<String, AdditionalBindingDetail>();
abr.docoShort = ext.getExtensionString("shortDoco"); if (compare && compList!=null) {
abr.usage = (ext.hasExtension("usage")) && ext.getExtensionByUrl("usage").hasValueUsageContext() ? ext.getExtensionByUrl("usage").getValueUsageContext() : null; for (Extension ext : compList) {
abr.any = "any".equals(ext.getExtensionString("scope")); AdditionalBindingDetail abr = additionalBinding(ext);
abr.unchanged = ext.hasUserData(ProfileUtilities.DERIVATION_EQUALS); if (compBindings.containsKey(abr.getKey())) {
bindings.add(abr); abr.incrementCount();
}
compBindings.put(abr.getKey(), abr);
}
} }
}
for (Extension ext : list) {
AdditionalBindingDetail abr = additionalBinding(ext);
if (compare && compList!=null) {
AdditionalBindingDetail match = null;
do {
match = compBindings.get(abr.getKey());
if (abr.alreadyMatched())
abr.incrementCount();
} while (match!=null && abr.alreadyMatched());
if (match!=null)
abr.setCompare(match);
bindings.add(abr);
if (abr.compare!=null)
compBindings.remove(abr.compare.getKey());
} else
bindings.add(abr);
}
for (AdditionalBindingDetail b: compBindings.values()) {
b.removed = true;
bindings.add(b);
}
}
protected AdditionalBindingDetail additionalBinding(Extension ext) {
AdditionalBindingDetail abr = new AdditionalBindingDetail();
abr.purpose = ext.getExtensionString("purpose");
abr.valueSet = ext.getExtensionString("valueSet");
abr.doco = ext.getExtensionString("documentation");
abr.docoShort = ext.getExtensionString("shortDoco");
abr.usage = (ext.hasExtension("usage")) && ext.getExtensionByUrl("usage").hasValueUsageContext() ? ext.getExtensionByUrl("usage").getValueUsageContext() : null;
abr.any = "any".equals(ext.getExtensionString("scope"));
abr.isUnchanged = ext.hasUserData(ProfileUtilities.DERIVATION_EQUALS);
return abr;
}
public String render() throws IOException { public String render() throws IOException {
if (bindings.isEmpty()) { if (bindings.isEmpty()) {
@ -109,12 +198,11 @@ public class AdditionalBindingsRenderer {
boolean usage = false; boolean usage = false;
boolean any = false; boolean any = false;
for (AdditionalBindingDetail binding : bindings) { for (AdditionalBindingDetail binding : bindings) {
doco = doco || (fullDoco && binding.doco != null) || (!fullDoco && binding.docoShort != null) ; doco = doco || binding.getDoco(fullDoco)!=null || (binding.compare!=null && binding.compare.getDoco(fullDoco)!=null);
usage = usage || binding.usage != null; usage = usage || binding.usage != null || (binding.compare!=null && binding.compare.usage!=null);
any = any || binding.any; any = any || binding.any || (binding.compare!=null && binding.compare.any);
} }
XhtmlNode tr = new XhtmlNode(NodeType.Element, "tr"); XhtmlNode tr = new XhtmlNode(NodeType.Element, "tr");
children.add(tr); children.add(tr);
tr.td().style("font-size: 11px").b().tx("Additional Bindings"); tr.td().style("font-size: 11px").b().tx("Additional Bindings");
@ -130,37 +218,62 @@ public class AdditionalBindingsRenderer {
} }
for (AdditionalBindingDetail binding : bindings) { for (AdditionalBindingDetail binding : bindings) {
tr = new XhtmlNode(NodeType.Element, "tr"); tr = new XhtmlNode(NodeType.Element, "tr");
if (binding.unchanged) { if (binding.unchanged()) {
tr.style("opacity: 0.5"); tr.style(STYLE_REMOVED);
} else if (binding.removed) {
tr.style(STYLE_REMOVED);
} }
children.add(tr); children.add(tr);
BindingResolution br = pkp == null ? makeNullBr(binding) : pkp.resolveBinding(profile, binding.valueSet, path); BindingResolution br = pkp == null ? makeNullBr(binding) : pkp.resolveBinding(profile, binding.valueSet, path);
BindingResolution compBr = null;
if (binding.compare!=null && binding.compare.valueSet!=null)
compBr = pkp == null ? makeNullBr(binding.compare) : pkp.resolveBinding(profile, binding.compare.valueSet, path);
XhtmlNode valueset = tr.td().style("font-size: 11px");
if (binding.compare!=null && binding.valueSet.equals(binding.compare.valueSet))
valueset.style(STYLE_UNCHANGED);
if (br.url != null) { if (br.url != null) {
tr.td().style("font-size: 11px").ah(Utilities.isAbsoluteUrl(br.url) || !pkp.prependLinks() ? br.url : corePath+br.url, binding.valueSet).tx(br.display); valueset.ah(determineUrl(br.url), binding.valueSet).tx(br.display);
} else { } else {
tr.td().style("font-size: 11px").span(null, binding.valueSet).tx(br.display); valueset.span(null, binding.valueSet).tx(br.display);
}
if (binding.compare!=null && binding.compare.valueSet!=null && !binding.valueSet.equals(binding.compare.valueSet)) {
valueset.br();
valueset = valueset.span(STYLE_REMOVED, null);
if (compBr.url != null) {
valueset.ah(determineUrl(compBr.url), binding.compare.valueSet).tx(compBr.display);
} else {
valueset.span(null, binding.compare.valueSet).tx(compBr.display);
}
}
XhtmlNode purpose = tr.td().style("font-size: 11px");
if (binding.compare!=null && binding.purpose.equals(binding.compare.purpose))
purpose.style("font-color: darkgray");
renderPurpose(purpose, binding.purpose);
if (binding.compare!=null && binding.compare.purpose!=null && !binding.purpose.equals(binding.compare.purpose)) {
purpose.br();
purpose = purpose.span(STYLE_UNCHANGED, null);
renderPurpose(purpose, binding.compare.purpose);
} }
renderPurpose(tr.td().style("font-size: 11px"), binding.purpose);
if (usage) { if (usage) {
if (binding.usage != null) { if (binding.usage != null) {
// TODO: This isn't rendered at all yet. Ideally, we want it to render with comparison...
new DataRenderer(context).render(tr.td(), binding.usage); new DataRenderer(context).render(tr.td(), binding.usage);
} else { } else {
tr.td(); tr.td();
} }
} }
if (any) { if (any) {
if (binding.any) { String newRepeat = binding.any ? "Any repeats" : "All repeats";
tr.td().style("font-size: 11px").tx("Any repeat"); String oldRepeat = binding.compare!=null && binding.compare.any ? "Any repeats" : "All repeats";
} else { compareString(tr.td().style("font-size: 11px"), newRepeat, oldRepeat);
tr.td().style("font-size: 11px").tx("All repeats");
}
} }
if (doco) { if (doco) {
String ds = fullDoco ? binding.doco : binding.docoShort; if (binding.doco != null) {
if (ds != null) { String d = md.processMarkdown("Binding.description", fullDoco ? binding.doco : binding.docoShort);
String d = fullDoco ? md.processMarkdown("Binding.description", ds) : ds; String oldD = binding.compare==null ? null : md.processMarkdown("Binding.description.compare", fullDoco ? binding.compare.doco : binding.compare.docoShort);
tr.td().style("font-size: 11px").innerHTML(d); tr.td().style("font-size: 11px").innerHTML(compareHtml(d, oldD));
} else { } else {
tr.td().style("font-size: 11px"); tr.td().style("font-size: 11px");
} }
@ -168,6 +281,28 @@ public class AdditionalBindingsRenderer {
} }
} }
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 String determineUrl(String url) {
return Utilities.isAbsoluteUrl(url) || !pkp.prependLinks() ? url : corePath + url;
}
private void renderPurpose(XhtmlNode td, String purpose) { private void renderPurpose(XhtmlNode td, String purpose) {
switch (purpose) { switch (purpose) {
case "maximum": case "maximum":

View File

@ -325,7 +325,8 @@ public class ProfileUtilities extends TranslatingUtilities {
public static final int STATUS_ERROR = 3; public static final int STATUS_ERROR = 3;
public static final int STATUS_FATAL = 4; public static final int STATUS_FATAL = 4;
public static final String BASE_MODEL = "base.model";
public static final String BASE_PATH = "base.path";
public static final String DERIVATION_EQUALS = "derivation.equals"; public static final String DERIVATION_EQUALS = "derivation.equals";
public static final String DERIVATION_POINTER = "derived.pointer"; public static final String DERIVATION_POINTER = "derived.pointer";
public static final String IS_DERIVED = "derived.fact"; public static final String IS_DERIVED = "derived.fact";
@ -430,8 +431,6 @@ public class ProfileUtilities extends TranslatingUtilities {
String getLinkForUrl(String corePath, String s); String getLinkForUrl(String corePath, String s);
} }
public List<ElementDefinition> getChildMap(StructureDefinition profile, ElementDefinition element) throws DefinitionException { public List<ElementDefinition> getChildMap(StructureDefinition profile, ElementDefinition element) throws DefinitionException {
if (childMapCache .containsKey(element)) { if (childMapCache .containsKey(element)) {
return childMapCache.get(element); return childMapCache.get(element);
@ -504,7 +503,7 @@ public class ProfileUtilities extends TranslatingUtilities {
/** /**
* Given a Structure, navigate to the element given by the path and return the direct children of that element * Given a Structure, navigate to the element given by the path and return the direct children of that element
* *
* @param structure The structure to navigate into * @param profile The structure to navigate into
* @param path The path of the element within the structure to get the children for * @param path The path of the element within the structure to get the children for
* @return A List containing the element children (all of them are Elements) * @return A List containing the element children (all of them are Elements)
*/ */
@ -615,10 +614,9 @@ public class ProfileUtilities extends TranslatingUtilities {
* Given a base (snapshot) profile structure, and a differential profile, generate a new snapshot profile * Given a base (snapshot) profile structure, and a differential profile, generate a new snapshot profile
* *
* @param base - the base structure on which the differential will be applied * @param base - the base structure on which the differential will be applied
* @param differential - the differential to apply to the base * @param derived - the differential to apply to the base
* @param url - where the base has relative urls for profile references, these need to be converted to absolutes by prepending this URL (e.g. the canonical URL) * @param url - where the base has relative urls for profile references, these need to be converted to absolutes by prepending this URL (e.g. the canonical URL)
* @param webUrl - where the base has relative urls in markdown, these need to be converted to absolutes by prepending this URL (this is not the same as the canonical URL) * @param webUrl - where the base has relative urls in markdown, these need to be converted to absolutes by prepending this URL (this is not the same as the canonical URL)
* @param trimDifferential - if this is true, then the snap short generator will remove any material in the element definitions that is not different to the base
* @return * @return
* @throws FHIRException * @throws FHIRException
* @throws DefinitionException * @throws DefinitionException
@ -1142,7 +1140,7 @@ public class ProfileUtilities extends TranslatingUtilities {
// so we just copy it in // so we just copy it in
ElementDefinition outcome = updateURLs(url, webUrl, currentBase.copy()); ElementDefinition outcome = updateURLs(url, webUrl, currentBase.copy());
outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc)); outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc));
updateFromBase(outcome, currentBase); updateFromBase(outcome, currentBase, srcSD.getUrl());
updateConstraintSources(outcome, srcSD.getUrl()); updateConstraintSources(outcome, srcSD.getUrl());
markDerived(outcome); markDerived(outcome);
if (resultPathBase == null) if (resultPathBase == null)
@ -1289,7 +1287,7 @@ public class ProfileUtilities extends TranslatingUtilities {
outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc)); outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc));
if (res == null) if (res == null)
res = outcome; res = outcome;
updateFromBase(outcome, currentBase); updateFromBase(outcome, currentBase, srcSD.getUrl());
if (diffMatches.get(0).hasSliceName()) { if (diffMatches.get(0).hasSliceName()) {
outcome.setSliceName(diffMatches.get(0).getSliceName()); outcome.setSliceName(diffMatches.get(0).getSliceName());
if (!diffMatches.get(0).hasMin() && (diffMatches.size() > 1 || slicer == null || slicer.getSlicing().getRules() != SlicingRules.CLOSED) && !currentBase.hasSliceName()) { if (!diffMatches.get(0).hasMin() && (diffMatches.size() > 1 || slicer == null || slicer.getSlicing().getRules() != SlicingRules.CLOSED) && !currentBase.hasSliceName()) {
@ -1552,7 +1550,7 @@ public class ProfileUtilities extends TranslatingUtilities {
// we're just going to accept the differential slicing at face value // we're just going to accept the differential slicing at face value
ElementDefinition outcome = updateURLs(url, webUrl, currentBase.copy()); ElementDefinition outcome = updateURLs(url, webUrl, currentBase.copy());
outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc)); outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc));
updateFromBase(outcome, currentBase); updateFromBase(outcome, currentBase, srcSD.getUrl());
if (!diffMatches.get(0).hasSlicing()) if (!diffMatches.get(0).hasSlicing())
outcome.setSlicing(makeExtensionSlicing()); outcome.setSlicing(makeExtensionSlicing());
@ -1627,7 +1625,7 @@ public class ProfileUtilities extends TranslatingUtilities {
// so we just copy it in // so we just copy it in
ElementDefinition outcome = updateURLs(url, webUrl, currentBase.copy()); ElementDefinition outcome = updateURLs(url, webUrl, currentBase.copy());
outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc)); outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc));
updateFromBase(outcome, currentBase); updateFromBase(outcome, currentBase, srcSD.getUrl());
markDerived(outcome); markDerived(outcome);
if (resultPathBase == null) if (resultPathBase == null)
resultPathBase = outcome.getPath(); resultPathBase = outcome.getPath();
@ -1664,6 +1662,8 @@ public class ProfileUtilities extends TranslatingUtilities {
if (!outcome.getPath().startsWith(resultPathBase)) if (!outcome.getPath().startsWith(resultPathBase))
throw new DefinitionException(context.formatMessage(I18nConstants.ADDING_WRONG_PATH_IN_PROFILE___VS_, profileName, outcome.getPath(), resultPathBase)); throw new DefinitionException(context.formatMessage(I18nConstants.ADDING_WRONG_PATH_IN_PROFILE___VS_, profileName, outcome.getPath(), resultPathBase));
result.getElement().add(outcome); // so we just copy it in result.getElement().add(outcome); // so we just copy it in
outcome.setUserData(BASE_MODEL, srcSD.getUrl());
outcome.setUserData(BASE_PATH, resultPathBase);
baseCursor++; baseCursor++;
} }
} }
@ -1824,7 +1824,7 @@ public class ProfileUtilities extends TranslatingUtilities {
} }
ElementDefinition outcome = updateURLs(url, webUrl, currentBase.copy()); ElementDefinition outcome = updateURLs(url, webUrl, currentBase.copy());
outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc)); outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc));
updateFromBase(outcome, currentBase); updateFromBase(outcome, currentBase, srcSD.getUrl());
if (diffMatches.get(0).hasSlicing() || !diffMatches.get(0).hasSliceName()) { if (diffMatches.get(0).hasSlicing() || !diffMatches.get(0).hasSliceName()) {
updateFromSlicing(outcome.getSlicing(), diffMatches.get(0).getSlicing()); updateFromSlicing(outcome.getSlicing(), diffMatches.get(0).getSlicing());
updateFromDefinition(outcome, diffMatches.get(0), profileName, closed, url, srcSD); // if there's no slice, we don't want to update the unsliced description updateFromDefinition(outcome, diffMatches.get(0), profileName, closed, url, srcSD); // if there's no slice, we don't want to update the unsliced description
@ -1876,7 +1876,7 @@ public class ProfileUtilities extends TranslatingUtilities {
for (ElementDefinition baseItem : baseMatches) { for (ElementDefinition baseItem : baseMatches) {
baseCursor = base.getElement().indexOf(baseItem); baseCursor = base.getElement().indexOf(baseItem);
outcome = updateURLs(url, webUrl, baseItem.copy()); outcome = updateURLs(url, webUrl, baseItem.copy());
updateFromBase(outcome, currentBase); updateFromBase(outcome, currentBase, srcSD.getUrl());
outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc)); outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc));
outcome.setSlicing(null); outcome.setSlicing(null);
if (!outcome.getPath().startsWith(resultPathBase)) if (!outcome.getPath().startsWith(resultPathBase))
@ -1903,6 +1903,8 @@ public class ProfileUtilities extends TranslatingUtilities {
outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc)); outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc));
if (!outcome.getPath().startsWith(resultPathBase)) if (!outcome.getPath().startsWith(resultPathBase))
throw new DefinitionException(context.formatMessage(I18nConstants.ADDING_WRONG_PATH)); throw new DefinitionException(context.formatMessage(I18nConstants.ADDING_WRONG_PATH));
outcome.setUserData(BASE_PATH, outcome.getPath());
outcome.setUserData(BASE_MODEL, srcSD.getUrl());
result.getElement().add(outcome); result.getElement().add(outcome);
baseCursor++; baseCursor++;
} }
@ -1932,7 +1934,7 @@ public class ProfileUtilities extends TranslatingUtilities {
outcome = updateURLs(url, webUrl, currentBase.copy()); outcome = updateURLs(url, webUrl, currentBase.copy());
// outcome = updateURLs(url, diffItem.copy()); // outcome = updateURLs(url, diffItem.copy());
outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc)); outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc));
updateFromBase(outcome, currentBase); updateFromBase(outcome, currentBase, srcSD.getUrl());
outcome.setSlicing(null); outcome.setSlicing(null);
outcome.setMin(0); // we're in a slice, so it's only a mandatory if it's explicitly marked so outcome.setMin(0); // we're in a slice, so it's only a mandatory if it's explicitly marked so
if (!outcome.getPath().startsWith(resultPathBase)) if (!outcome.getPath().startsWith(resultPathBase))
@ -2439,7 +2441,9 @@ public class ProfileUtilities extends TranslatingUtilities {
} }
private void updateFromBase(ElementDefinition derived, ElementDefinition base) { private void updateFromBase(ElementDefinition derived, ElementDefinition base, String baseProfileUrl) {
derived.setUserData(BASE_MODEL, baseProfileUrl);
derived.setUserData(BASE_PATH, base.getPath());
if (base.hasBase()) { if (base.hasBase()) {
if (!derived.hasBase()) if (!derived.hasBase())
derived.setBase(new ElementDefinitionBaseComponent()); derived.setBase(new ElementDefinitionBaseComponent());
@ -3535,7 +3539,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (!child.getPath().endsWith(".id")) { if (!child.getPath().endsWith(".id")) {
List<StructureDefinition> sdl = new ArrayList<>(); List<StructureDefinition> sdl = new ArrayList<>();
sdl.add(ed); sdl.add(ed);
genElement(defFile == null ? "" : defFile+"-definitions.html#extension.", gen, r.getSubRows(), child, ed.getSnapshot().getElement(), sdl, true, defFile, true, full, corePath, imagePath, true, false, false, false, null, false, rc); genElement(defFile == null ? "" : defFile+"-definitions.html#extension.", gen, r.getSubRows(), child, ed.getSnapshot().getElement(), sdl, true, defFile, true, full, corePath, imagePath, true, false, false, false, null, false, rc, "");
} }
} else if (deep) { } else if (deep) {
List<ElementDefinition> children = new ArrayList<ElementDefinition>(); List<ElementDefinition> children = new ArrayList<ElementDefinition>();
@ -4016,19 +4020,14 @@ public class ProfileUtilities extends TranslatingUtilities {
return piece; return piece;
} }
public XhtmlNode generateTable(String defFile, StructureDefinition profile, boolean diff, String imageFolder, boolean inlineGraphics, String profileBaseFileName, boolean snapshot, String corePath, String imagePath, public XhtmlNode generateTable(String defFile, StructureDefinition profile, boolean diff, String imageFolder, boolean inlineGraphics, String profileBaseFileName, boolean snapshot, String corePath, String imagePath,
boolean logicalModel, boolean allInvariants, Set<String> outputTracker, boolean active, boolean mustSupport, RenderingContext rc) throws IOException, FHIRException { boolean logicalModel, boolean allInvariants, Set<String> outputTracker, boolean active, boolean mustSupport, RenderingContext rc) throws IOException, FHIRException {
assert(diff != snapshot);// check it's ok to get rid of one of these return generateTable(defFile, profile, diff, imageFolder, inlineGraphics, profileBaseFileName, snapshot, corePath, imagePath, logicalModel, allInvariants, outputTracker, active, mustSupport, rc, "");
HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics, true); }
gen.setTranslator(getTranslator());
TableModel model = gen.initNormalTable(corePath, false, true, profile.getId()+(diff ? "d" : "s"), active); public List<ElementDefinition> supplementMissingDiffElements(StructureDefinition profile) {
List<ElementDefinition> list = new ArrayList<>(); List<ElementDefinition> list = new ArrayList<>();
if (diff) list.addAll(profile.getDifferential().getElement());
list.addAll(profile.getDifferential().getElement());
else
list.addAll(profile.getSnapshot().getElement());
List<StructureDefinition> profiles = new ArrayList<StructureDefinition>();
profiles.add(profile);
if (list.isEmpty()) { if (list.isEmpty()) {
ElementDefinition root = new ElementDefinition().setPath(profile.getType()); ElementDefinition root = new ElementDefinition().setPath(profile.getType());
root.setId(profile.getType()); root.setId(profile.getType());
@ -4040,10 +4039,27 @@ public class ProfileUtilities extends TranslatingUtilities {
list.add(0, root); list.add(0, root);
} }
} }
if (diff) { insertMissingSparseElements(list);
insertMissingSparseElements(list); return list;
}
public XhtmlNode generateTable(String defFile, StructureDefinition profile, boolean diff, String imageFolder, boolean inlineGraphics, String profileBaseFileName, boolean snapshot, String corePath, String imagePath,
boolean logicalModel, boolean allInvariants, Set<String> outputTracker, boolean active, boolean mustSupport, RenderingContext rc, String anchorPrefix) throws IOException, FHIRException {
assert(diff != snapshot);// check it's ok to get rid of one of these
HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics, true);
gen.setTranslator(getTranslator());
TableModel model = gen.initNormalTable(corePath, false, true, profile.getId()+(diff ? "d" : "s"), active);
List<ElementDefinition> list;
if (diff)
list = supplementMissingDiffElements(profile);
else {
list = new ArrayList<>();
list.addAll(profile.getSnapshot().getElement());
} }
genElement(defFile == null ? null : defFile+"#", gen, model.getRows(), list.get(0), list, profiles, diff, profileBaseFileName, null, snapshot, corePath, imagePath, true, logicalModel, profile.getDerivation() == TypeDerivationRule.CONSTRAINT && usesMustSupport(list), allInvariants, null, mustSupport, rc); List<StructureDefinition> profiles = new ArrayList<StructureDefinition>();
profiles.add(profile);
genElement(defFile == null ? null : defFile+"#", gen, model.getRows(), list.get(0), list, profiles, diff, profileBaseFileName, null, snapshot, corePath, imagePath, true, logicalModel, profile.getDerivation() == TypeDerivationRule.CONSTRAINT && usesMustSupport(list), allInvariants, null, mustSupport, rc, anchorPrefix);
try { try {
return gen.generate(model, imagePath, 0, outputTracker); return gen.generate(model, imagePath, 0, outputTracker);
} catch (org.hl7.fhir.exceptions.FHIRException e) { } catch (org.hl7.fhir.exceptions.FHIRException e) {
@ -4139,7 +4155,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private Row genElement(String defPath, HierarchicalTableGenerator gen, List<Row> rows, ElementDefinition element, List<ElementDefinition> all, List<StructureDefinition> profiles, boolean showMissing, String profileBaseFileName, Boolean extensions, private Row genElement(String defPath, HierarchicalTableGenerator gen, List<Row> rows, ElementDefinition element, List<ElementDefinition> all, List<StructureDefinition> profiles, boolean showMissing, String profileBaseFileName, Boolean extensions,
boolean snapshot, String corePath, String imagePath, boolean root, boolean logicalModel, boolean isConstraintMode, boolean allInvariants, Row slicingRow, boolean mustSupport, RenderingContext rc) throws IOException, FHIRException { boolean snapshot, String corePath, String imagePath, boolean root, boolean logicalModel, boolean isConstraintMode, boolean allInvariants, Row slicingRow, boolean mustSupport, RenderingContext rc, String anchorPrefix) throws IOException, FHIRException {
Row originalRow = slicingRow; Row originalRow = slicingRow;
StructureDefinition profile = profiles == null ? null : profiles.get(profiles.size()-1); StructureDefinition profile = profiles == null ? null : profiles.get(profiles.size()-1);
Row typesRow = null; Row typesRow = null;
@ -4201,7 +4217,7 @@ public class ProfileUtilities extends TranslatingUtilities {
row.setOpacity("0.5"); row.setOpacity("0.5");
} }
UnusedTracker used = new UnusedTracker(); UnusedTracker used = new UnusedTracker();
String ref = defPath == null ? null : defPath + element.getId(); String ref = defPath == null ? null : defPath + anchorPrefix + element.getId();
String sName = tail(element.getPath()); String sName = tail(element.getPath());
if (element.hasSliceName()) if (element.hasSliceName())
sName = sName +":"+element.getSliceName(); sName = sName +":"+element.getSliceName();
@ -4276,7 +4292,7 @@ public class ProfileUtilities extends TranslatingUtilities {
Row childRow = chooseChildRowByGroup(gen, currRow, groups, child, element, isConstraintMode); Row childRow = chooseChildRowByGroup(gen, currRow, groups, child, element, isConstraintMode);
if (logicalModel || !child.getPath().endsWith(".id") || (child.getPath().endsWith(".id") && (profile != null) && (profile.getDerivation() == TypeDerivationRule.CONSTRAINT))) { if (logicalModel || !child.getPath().endsWith(".id") || (child.getPath().endsWith(".id") && (profile != null) && (profile.getDerivation() == TypeDerivationRule.CONSTRAINT))) {
currRow = genElement(defPath, gen, childRow.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, snapshot, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants, currRow, mustSupport, rc); currRow = genElement(defPath, gen, childRow.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, snapshot, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants, currRow, mustSupport, rc, anchorPrefix);
} }
} }
} }

View File

@ -28,7 +28,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
} }
public boolean render(XhtmlNode x, StructureDefinition sd) throws FHIRFormatError, DefinitionException, IOException { public boolean render(XhtmlNode x, StructureDefinition sd) throws FHIRFormatError, DefinitionException, IOException {
x.getChildNodes().add(context.getProfileUtilities().generateTable(context.getDefinitionsTarget(), sd, true, context.getDestDir(), false, sd.getId(), false, context.getSpecificationLink(), "", false, false, null, false, false, context)); x.getChildNodes().add(context.getProfileUtilities().generateTable(context.getDefinitionsTarget(), sd, true, context.getDestDir(), false, sd.getId(), false, context.getSpecificationLink(), "", false, false, null, false, false, context, ""));
return true; return true;
} }