r4b changes to match r5

This commit is contained in:
Grahame Grieve 2022-05-11 02:48:22 +10:00
parent be631a5130
commit 4b1b99a61c
6 changed files with 667 additions and 258 deletions

View File

@ -983,17 +983,17 @@ public class ProfileComparer extends CanonicalResourceComparer {
String leftColor = !combined.hasLeft() ? COLOR_NO_ROW_LEFT : combined.hasErrors() ? COLOR_DIFFERENT : null;
String rightColor = !combined.hasRight() ? COLOR_NO_ROW_LEFT : combined.hasErrors() ? COLOR_DIFFERENT : null;
if (combined.hasLeft()) {
nc = utilsRight.genElementNameCell(gen, combined.getLeft().getDef(), "??", true, corePath, prefix, root, false, false, combined.getLeft().getSrc(), typesRow, row, false, ext, used , ref, sName);
nc = utilsRight.genElementNameCell(gen, combined.getLeft().getDef(), "??", true, corePath, prefix, root, false, false, combined.getLeft().getSrc(), typesRow, row, false, ext, used , ref, sName, null);
} else {
nc = utilsRight.genElementNameCell(gen, combined.getRight().getDef(), "??", true, corePath, prefix, root, false, false, combined.getRight().getSrc(), typesRow, row, false, ext, used , ref, sName);
nc = utilsRight.genElementNameCell(gen, combined.getRight().getDef(), "??", true, corePath, prefix, root, false, false, combined.getRight().getSrc(), typesRow, row, false, ext, used , ref, sName, null);
}
if (combined.hasLeft()) {
frame(utilsRight.genElementCells(gen, combined.getLeft().getDef(), "??", true, corePath, prefix, root, false, false, combined.getLeft().getSrc(), typesRow, row, true, ext, used , ref, sName, nc, false, false), leftColor);
frame(utilsRight.genElementCells(gen, combined.getLeft().getDef(), "??", true, corePath, prefix, root, false, false, combined.getLeft().getSrc(), typesRow, row, true, ext, used , ref, sName, nc, false, false, null), leftColor);
} else {
frame(spacers(row, 4, gen), leftColor);
}
if (combined.hasRight()) {
frame(utilsRight.genElementCells(gen, combined.getRight().getDef(), "??", true, corePath, prefix, root, false, false, combined.getRight().getSrc(), typesRow, row, true, ext, used, ref, sName, nc, false, false), rightColor);
frame(utilsRight.genElementCells(gen, combined.getRight().getDef(), "??", true, corePath, prefix, root, false, false, combined.getRight().getSrc(), typesRow, row, true, ext, used, ref, sName, nc, false, false, null), rightColor);
} else {
frame(spacers(row, 4, gen), rightColor);
}

View File

@ -0,0 +1,207 @@
package org.hl7.fhir.r4b.conformance;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r4b.conformance.AdditionalBindingsRenderer.AdditionalBindingDetail;
import org.hl7.fhir.r4b.conformance.ProfileUtilities.ProfileKnowledgeProvider;
import org.hl7.fhir.r4b.conformance.ProfileUtilities.ProfileKnowledgeProvider.BindingResolution;
import org.hl7.fhir.r4b.model.Extension;
import org.hl7.fhir.r4b.model.StructureDefinition;
import org.hl7.fhir.r4b.model.UsageContext;
import org.hl7.fhir.r4b.renderers.DataRenderer;
import org.hl7.fhir.r4b.renderers.IMarkdownProcessor;
import org.hl7.fhir.r4b.renderers.utils.RenderingContext;
import org.hl7.fhir.r4b.utils.PublicationHacker;
import org.hl7.fhir.r4b.model.ElementDefinition.ElementDefinitionBindingComponent;
import org.hl7.fhir.utilities.Utilities;
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;
public class AdditionalBindingsRenderer {
public class AdditionalBindingDetail {
private String purpose;
private String valueSet;
private String doco;
private UsageContext usage;
private boolean any;
private boolean unchanged;
}
private List<AdditionalBindingDetail> bindings = new ArrayList<>();
private ProfileKnowledgeProvider pkp;
private String corePath;
private StructureDefinition profile;
private String path;
private RenderingContext context;
private IMarkdownProcessor md;
public AdditionalBindingsRenderer(ProfileKnowledgeProvider pkp, String corePath, StructureDefinition profile, String path, RenderingContext context, IMarkdownProcessor md) {
this.pkp = pkp;
this.corePath = corePath;
this.profile = profile;
this.path = path;
this.context = context;
this.md = md;
}
public void seeMaxBinding(Extension ext) {
AdditionalBindingDetail abr = new AdditionalBindingDetail();
abr.purpose = "maximum";
abr.valueSet = ext.getValue().primitiveValue();
abr.unchanged = ext.hasUserData(ProfileUtilities.DERIVATION_EQUALS);
bindings.add(abr);
}
public void seeMinBinding(Extension ext) {
AdditionalBindingDetail abr = new AdditionalBindingDetail();
abr.purpose = "minimum";
abr.valueSet = ext.getValue().primitiveValue();
abr.unchanged = ext.hasUserData(ProfileUtilities.DERIVATION_EQUALS);
bindings.add(abr);
}
public void seeAdditionalBindings(List<Extension> list) {
for (Extension ext : list) {
AdditionalBindingDetail abr = new AdditionalBindingDetail();
abr.purpose = ext.getExtensionString("purpose");
abr.valueSet = ext.getExtensionString("valueSet");
abr.doco = ext.getExtensionString("documentation");
abr.usage = (ext.hasExtension("usage")) && ext.getExtensionByUrl("usage").hasValueUsageContext() ? ext.getExtensionByUrl("usage").getValueUsageContext() : null;
abr.any = "any".equals(ext.getExtensionString("scope"));
abr.unchanged = ext.hasUserData(ProfileUtilities.DERIVATION_EQUALS);
bindings.add(abr);
}
}
public String render() throws IOException {
if (bindings.isEmpty()) {
return "";
} else {
XhtmlNode tbl = new XhtmlNode(NodeType.Element, "table");
tbl.attribute("class", "grid");
render(tbl.getChildNodes(), true);
return new XhtmlComposer(false).compose(tbl);
}
}
public void render(HierarchicalTableGenerator gen, Cell c) throws FHIRFormatError, DefinitionException, IOException {
if (bindings.isEmpty()) {
return;
} else {
Piece piece = gen.new Piece("table").attr("class", "grid");
c.getPieces().add(piece);
render(piece.getChildren(), false);
}
}
private void render(List<XhtmlNode> children, boolean doDoco) throws FHIRFormatError, DefinitionException, IOException {
boolean doco = false;
boolean usage = false;
boolean any = false;
for (AdditionalBindingDetail binding : bindings) {
doco = doco || (doDoco && binding.doco != null);
usage = usage || binding.usage != null;
any = any || binding.any;
}
XhtmlNode tr = new XhtmlNode(NodeType.Element, "tr");
children.add(tr);
tr.td().style("font-size: 11px").b().tx("Additional Bindings");
tr.td().style("font-size: 11px").tx("Purpose");
if (usage) {
tr.td().style("font-size: 11px").tx("Usage");
}
if (any) {
tr.td().style("font-size: 11px").tx("Any");
}
if (doco) {
tr.td().style("font-size: 11px").tx("Documentation");
}
for (AdditionalBindingDetail binding : bindings) {
tr = new XhtmlNode(NodeType.Element, "tr");
if (binding.unchanged) {
tr.style("opacity: 0.5");
}
children.add(tr);
BindingResolution br = pkp == null ? makeNullBr(binding) : pkp.resolveBinding(profile, binding.valueSet, path);
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);
} else {
tr.td().style("font-size: 11px").span(null, binding.valueSet).tx(br.display);
}
renderPurpose(tr.td().style("font-size: 11px"), binding.purpose);
if (usage) {
if (binding.usage != null) {
new DataRenderer(context).render(tr.td(), binding.usage);
} else {
tr.td();
}
}
if (any) {
if (binding.any) {
tr.td().style("font-size: 11px").tx("Any repeat");
} else {
tr.td().style("font-size: 11px").tx("All repeats");
}
}
if (doco) {
if (binding.doco != null) {
String d = md.processMarkdown("Binding.description", binding.doco);
tr.td().style("font-size: 11px").innerHTML(d);
} else {
tr.td().style("font-size: 11px");
}
}
}
}
private void renderPurpose(XhtmlNode td, String purpose) {
switch (purpose) {
case "maximum":
td.ah(corePath+"extension-elementdefinition-maxvalueset.html", "A required binding, for use when the binding strength is 'extensible' or 'preferred'").tx("Max Binding");
break;
case "minimum":
td.ah(corePath+"extension-elementdefinition-minvalueset.html", "The minimum allowable value set - any conformant system SHALL support all these codes").tx("Min Binding");
break;
case "conformance" :
td.ah(corePath+"terminologies.html#strength", "Validators will check this binding (strength = required)").tx("Validation Criteria");
break;
case "current" :
td.span(null, "New records are required to use this value set, but legacy records may use other codes").tx("Required");
break;
case "recommended" :
td.span(null, "This is the value set that is recommended (documentation should explain why)").tx("Recommended");
break;
case "ui" :
td.span(null, "This value set is provided to user look up in a given context").tx("UI");
break;
case "starter" :
td.span(null, "This value set is a good set of codes to start with when designing your system").tx("Starter");
break;
case "component" :
td.span(null, "This value set is a component of the base value set").tx("Component");
break;
default:
td.span(null, "Unknown code for purpose").tx(purpose);
}
}
private BindingResolution makeNullBr(AdditionalBindingDetail binding) {
BindingResolution br = new BindingResolution();
br.url = "http://none.none/none";
br.display = "todo";
return br;
}
}

View File

@ -106,6 +106,7 @@ import org.hl7.fhir.r4b.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r4b.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r4b.renderers.TerminologyRenderer;
import org.hl7.fhir.r4b.renderers.spreadsheets.SpreadsheetGenerator;
import org.hl7.fhir.r4b.renderers.utils.RenderingContext;
import org.hl7.fhir.r4b.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r4b.utils.FHIRLexer;
import org.hl7.fhir.r4b.utils.FHIRPathEngine;
@ -309,7 +310,7 @@ public class ProfileUtilities extends TranslatingUtilities {
public static final int STATUS_FATAL = 4;
private 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 IS_DERIVED = "derived.fact";
public static final String UD_ERROR_STATUS = "error-status";
@ -1588,11 +1589,17 @@ public class ProfileUtilities extends TranslatingUtilities {
StructureDefinition dt = getTypeForElement(differential, diffCursor, profileName, diffMatches, outcome, webUrl);
contextName = dt.getUrl();
int start = diffCursor;
while (differential.getElement().size() > diffCursor && pathStartsWith(differential.getElement().get(diffCursor).getPath(), cpath+"."))
if (differential.getElement().get(diffCursor).getPath().equals(cpath)) {
diffCursor++;
}
while (differential.getElement().size() > diffCursor && pathStartsWith(differential.getElement().get(diffCursor).getPath(), cpath+".")) {
diffCursor++;
}
if (diffCursor > start) {
processPaths(indent+" ", result, dt.getSnapshot(), differential, 1 /* starting again on the data type, but skip the root */, start, dt.getSnapshot().getElement().size()-1,
diffCursor-1, url, getWebUrl(dt, webUrl, indent), profileName, cpath, outcome.getPath(), trimDifferential, contextName, resultPathBase, false, null, null, redirector, srcSD);
}
}
baseCursor++;
} else {
// the differential doesn't say anything about this item
@ -3432,7 +3439,7 @@ public class ProfileUtilities extends TranslatingUtilities {
return !p.contains(".");
}
public XhtmlNode generateExtensionTable(String defFile, StructureDefinition ed, String imageFolder, boolean inlineGraphics, boolean full, String corePath, String imagePath, Set<String> outputTracker) throws IOException, FHIRException {
public XhtmlNode generateExtensionTable(String defFile, StructureDefinition ed, String imageFolder, boolean inlineGraphics, boolean full, String corePath, String imagePath, Set<String> outputTracker, RenderingContext rc) throws IOException, FHIRException {
HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics, true);
gen.setTranslator(getTranslator());
TableModel model = gen.initNormalTable(corePath, false, true, ed.getId()+(full ? "f" : "n"), true);
@ -3470,7 +3477,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (!child.getPath().endsWith(".id")) {
List<StructureDefinition> sdl = new ArrayList<>();
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);
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) {
List<ElementDefinition> children = new ArrayList<ElementDefinition>();
@ -3795,6 +3802,9 @@ public class ProfileUtilities extends TranslatingUtilities {
if (contentReference.contains("#")) {
String url = contentReference.substring(0, contentReference.indexOf("#"));
contentReference = contentReference.substring(contentReference.indexOf("#"));
if (Utilities.noString(url)) {
url = source.getUrl();
}
if (!url.equals(source.getUrl())) {
source = context.fetchResource(StructureDefinition.class, url);
if (source == null) {
@ -3945,7 +3955,7 @@ public class ProfileUtilities extends TranslatingUtilities {
}
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) 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
HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics, true);
gen.setTranslator(getTranslator());
@ -3971,7 +3981,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (diff) {
insertMissingSparseElements(list);
}
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);
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);
try {
return gen.generate(model, imagePath, 0, outputTracker);
} catch (org.hl7.fhir.exceptions.FHIRException e) {
@ -4067,7 +4077,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,
boolean snapshot, String corePath, String imagePath, boolean root, boolean logicalModel, boolean isConstraintMode, boolean allInvariants, Row slicingRow, boolean mustSupport) throws IOException, FHIRException {
boolean snapshot, String corePath, String imagePath, boolean root, boolean logicalModel, boolean isConstraintMode, boolean allInvariants, Row slicingRow, boolean mustSupport, RenderingContext rc) throws IOException, FHIRException {
Row originalRow = slicingRow;
StructureDefinition profile = profiles == null ? null : profiles.get(profiles.size()-1);
Row typesRow = null;
@ -4136,8 +4146,8 @@ public class ProfileUtilities extends TranslatingUtilities {
used.used = true;
if (logicalModel && element.hasRepresentation(PropertyRepresentation.XMLATTR))
sName = "@"+sName;
Cell nc = genElementNameCell(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName);
genElementCells(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, nc, mustSupport, true);
Cell nc = genElementNameCell(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, all);
genElementCells(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, nc, mustSupport, true, rc);
if (element.hasSlicing()) {
if (standardExtensionSlicing(element)) {
used.used = true; // doesn't matter whether we have a type, we're used if we're setting up slicing ... element.hasType() && element.getType().get(0).hasProfile();
@ -4203,7 +4213,7 @@ public class ProfileUtilities extends TranslatingUtilities {
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))) {
currRow = genElement(defPath, gen, childRow.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, snapshot, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants, currRow, mustSupport);
currRow = genElement(defPath, gen, childRow.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, snapshot, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants, currRow, mustSupport, rc);
}
}
// if (!snapshot && (extensions == null || !extensions))
@ -4248,14 +4258,14 @@ public class ProfileUtilities extends TranslatingUtilities {
public Cell genElementNameCell(HierarchicalTableGenerator gen, ElementDefinition element, String profileBaseFileName, boolean snapshot, String corePath,
String imagePath, boolean root, boolean logicalModel, boolean allInvariants, StructureDefinition profile, Row typesRow, Row row, boolean hasDef,
boolean ext, UnusedTracker used, String ref, String sName) throws IOException {
boolean ext, UnusedTracker used, String ref, String sName, List<ElementDefinition> elements) throws IOException {
String hint = "";
hint = checkAdd(hint, (element.hasSliceName() ? translate("sd.table", "Slice")+" "+element.getSliceName() : ""));
if (hasDef && element.hasDefinition()) {
hint = checkAdd(hint, (hasDef && element.hasSliceName() ? ": " : ""));
hint = checkAdd(hint, !hasDef ? null : gt(element.getDefinitionElement()));
}
if (element.hasSlicing()) {
if (element.hasSlicing() && slicesExist(elements, element)) { // some elements set up slicing but don't actually slice, so we don't augment the name
sName = "Slices for "+sName;
}
Cell left = gen.new Cell(null, ref, sName, hint, null);
@ -4263,9 +4273,32 @@ public class ProfileUtilities extends TranslatingUtilities {
return left;
}
private boolean slicesExist(List<ElementDefinition> elements, ElementDefinition element) {
if (elements == null) {
return true;
}
boolean found = false;
int start = elements.indexOf(element);
if (start < 0) {
return false;
}
for (int i = start; i < elements.size(); i++) {
ElementDefinition ed = elements.get(i);
if (ed.getPath().equals(element.getPath())) {
if (ed.hasSliceName()) {
found = true;
}
}
if (ed.getPath().length() < element.getPath().length()) {
break;
}
}
return found;
}
public List<Cell> genElementCells(HierarchicalTableGenerator gen, ElementDefinition element, String profileBaseFileName, boolean snapshot, String corePath,
String imagePath, boolean root, boolean logicalModel, boolean allInvariants, StructureDefinition profile, Row typesRow, Row row, boolean hasDef,
boolean ext, UnusedTracker used, String ref, String sName, Cell nameCell, boolean mustSupport, boolean allowSubRows) throws IOException {
boolean ext, UnusedTracker used, String ref, String sName, Cell nameCell, boolean mustSupport, boolean allowSubRows, RenderingContext rc) throws IOException {
List<Cell> res = new ArrayList<>();
Cell gc = gen.new Cell();
row.getCells().add(gc);
@ -4291,7 +4324,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (extDefn == null) {
res.add(genCardinality(gen, element, row, hasDef, used, null));
res.add(addCell(row, gen.new Cell(null, null, "?gen-e1? "+element.getType().get(0).getProfile(), null, null)));
res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, profile == null ? "" : profile.getUrl(), eurl, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows));
res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, profile == null ? "" : profile.getUrl(), eurl, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows, rc));
} else {
String name = urltail(eurl);
nameCell.getPieces().get(0).setText(name);
@ -4304,7 +4337,7 @@ public class ProfileUtilities extends TranslatingUtilities {
else // if it's complex, we just call it nothing
// genTypes(gen, row, extDefn.getSnapshot().getElement().get(0), profileBaseFileName, profile);
res.add(addCell(row, gen.new Cell(null, null, "("+translate("sd.table", "Complex")+")", null, null)));
res.add(generateDescription(gen, row, element, extDefn.getElement(), used.used, null, extDefn.getUrl(), profile, corePath, imagePath, root, logicalModel, allInvariants, valueDefn, snapshot, mustSupport, allowSubRows));
res.add(generateDescription(gen, row, element, extDefn.getElement(), used.used, null, extDefn.getUrl(), profile, corePath, imagePath, root, logicalModel, allInvariants, valueDefn, snapshot, mustSupport, allowSubRows, rc));
}
} else {
res.add(genCardinality(gen, element, row, hasDef, used, null));
@ -4312,7 +4345,7 @@ public class ProfileUtilities extends TranslatingUtilities {
res.add(addCell(row, gen.new Cell()));
else
res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, mustSupport));
res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows));
res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows, rc));
}
} else {
res.add(genCardinality(gen, element, row, hasDef, used, null));
@ -4320,7 +4353,7 @@ public class ProfileUtilities extends TranslatingUtilities {
res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, mustSupport));
else
res.add(addCell(row, gen.new Cell()));
res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows));
res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows, rc));
}
return res;
}
@ -4626,11 +4659,11 @@ public class ProfileUtilities extends TranslatingUtilities {
&& element.getSlicing().getRules() != SlicingRules.CLOSED && element.getSlicing().getDiscriminator().size() == 1 && element.getSlicing().getDiscriminator().get(0).getPath().equals("url") && element.getSlicing().getDiscriminator().get(0).getType().equals(DiscriminatorType.VALUE);
}
private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows) throws IOException, FHIRException {
return generateDescription(gen, row, definition, fallback, used, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot, mustSupportOnly, allowSubRows);
private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows, RenderingContext rc) throws IOException, FHIRException {
return generateDescription(gen, row, definition, fallback, used, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot, mustSupportOnly, allowSubRows, rc);
}
private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, ElementDefinition valueDefn, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows) throws IOException, FHIRException {
private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, ElementDefinition valueDefn, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows, RenderingContext rc) throws IOException, FHIRException {
Cell c = gen.new Cell();
row.getCells().add(c);
@ -4749,29 +4782,22 @@ public class ProfileUtilities extends TranslatingUtilities {
c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(corePath+"terminologies.html#"+binding.getStrength().toCode(), egt(binding.getStrengthElement()), binding.getStrength().getDefinition())));
c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(null, ")", null)));
}
if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) {
br = pkp == null ? makeNullBr(binding) : pkp.resolveBinding(profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), definition.getPath());
c.addPiece(gen.new Piece("br"));
c.getPieces().add(checkForNoChange(binding, gen.new Piece(corePath+"extension-elementdefinition-maxvalueset.html", translate("sd.table", "Max Binding")+": ", "Max Value Set Extension").addStyle("font-weight:bold")));
c.getPieces().add(checkForNoChange(binding, gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !pkp.prependLinks() ? br.url : corePath+br.url, br.display, null)));
}
if (binding.hasExtension(ToolingExtensions.EXT_MIN_VALUESET)) {
br = pkp.resolveBinding(profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MIN_VALUESET), definition.getPath());
c.addPiece(gen.new Piece("br"));
c.getPieces().add(checkForNoChange(binding, gen.new Piece(corePath+"extension-elementdefinition-minvalueset.html", translate("sd.table", "Min Binding")+": ", "Min Value Set Extension").addStyle("font-weight:bold")));
c.getPieces().add(checkForNoChange(binding, gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !pkp.prependLinks() ? br.url : corePath+br.url, br.display, null)));
}
if (binding.hasDescription() && MarkDownProcessor.isSimpleMarkdown(binding.getDescription())) {
c.getPieces().add(gen.new Piece(null, ": ", null));
c.addMarkdownNoPara(PublicationHacker.fixBindingDescriptions(context, binding.getDescriptionElement()).asStringValue(), checkForNoChange(PublicationHacker.fixBindingDescriptions(context, binding.getDescriptionElement())));
}
if (binding.hasExtension(ToolingExtensions.EXT_BINDING_ADDITIONAL)) {
c.addPiece(gen.new Piece("br"));
c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, translate("sd.table", "Additional Bindings")+": ", null).addStyle("font-weight:bold")));
for (Extension ext : binding.getExtensionsByUrl(ToolingExtensions.EXT_BINDING_ADDITIONAL)) {
renderAdditionalBinding(gen, c, ext);
}
AdditionalBindingsRenderer abr = new AdditionalBindingsRenderer(pkp, corePath, profile, definition.getPath(), rc, null);
if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) {
abr.seeMaxBinding(ToolingExtensions.getExtension(binding, ToolingExtensions.EXT_MAX_VALUESET));
}
if (binding.hasExtension(ToolingExtensions.EXT_MIN_VALUESET)) {
abr.seeMinBinding(ToolingExtensions.getExtension(binding, ToolingExtensions.EXT_MIN_VALUESET));
}
if (binding.hasExtension(ToolingExtensions.EXT_BINDING_ADDITIONAL)) {
abr.seeAdditionalBindings(binding.getExtensionsByUrl(ToolingExtensions.EXT_BINDING_ADDITIONAL));
}
abr.render(gen, c);
}
for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) {
if (!inv.hasSource() || profile == null || inv.getSource().equals(profile.getUrl()) || allInvariants) {
@ -4851,23 +4877,6 @@ public class ProfileUtilities extends TranslatingUtilities {
return c;
}
private void renderAdditionalBinding(HierarchicalTableGenerator gen, Cell c, Extension ext) {
// <nsbp>2 <sp> purpose <sp> value-set-link ([context]) {documentation}
String purpose = ext.getExtensionString("purpose");
String valueSet = ext.getExtensionString("valueSet");
String doco = ext.getExtensionString("documentation");
//UsageContext usage = (ext.hasExtension("usage")) ? ext.getExtensionByUrl("usage").getValueUsageContext() : null;
//
// purpose: code - defines how the binding is used
// usage : UsageContext - defines the contexts in which this binding is used for it's purpose
// valueSet : canonical(ValueSet)
// documentation : markdown
// !!
// c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getKey()+": ", null).addStyle("font-weight:bold")));
// c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, gt(inv.getHumanElement()), null)));
}
private BindingResolution makeNullBr(ElementDefinitionBindingComponent binding) {
BindingResolution br = new BindingResolution();
br.url = "http://none.none/none";
@ -6815,5 +6824,4 @@ public class ProfileUtilities extends TranslatingUtilities {
this.masterSourceFileNames = masterSourceFileNames;
}
}
}

View File

@ -335,6 +335,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private boolean autoFixSliceNames;
private XVerExtensionManager xver;
private boolean wantFixDifferentialFirstElementType;
private Set<String> masterSourceFileNames;
public ProfileUtilities(IWorkerContext context, List<ValidationMessage> messages, ProfileKnowledgeProvider pkp, FHIRPathEngine fpe) {
super();
@ -548,7 +549,17 @@ public class ProfileUtilities extends TranslatingUtilities {
}
public List<ElementDefinition> getChildList(StructureDefinition structure, ElementDefinition element) {
return getChildList(structure, element.getPath(), element.getId(), false);
if (element.hasContentReference()) {
ElementDefinition target = element;
for (ElementDefinition t : structure.getSnapshot().getElement()) {
if (t.getId().equals(element.getContentReference().substring(1))) {
target = t;
}
}
return getChildList(structure, target.getPath(), target.getId(), false);
} else {
return getChildList(structure, element.getPath(), element.getId(), false);
}
}
public void updateMaps(StructureDefinition base, StructureDefinition derived) throws DefinitionException {
@ -612,174 +623,178 @@ public class ProfileUtilities extends TranslatingUtilities {
}
derived.setUserData("profileutils.snapshot.generating", true);
snapshotStack.add(derived.getUrl());
if (!Utilities.noString(webUrl) && !webUrl.endsWith("/"))
webUrl = webUrl + '/';
if (defWebRoot == null)
defWebRoot = webUrl;
derived.setSnapshot(new StructureDefinitionSnapshotComponent());
try {
checkDifferential(derived.getDifferential().getElement(), typeName(derived.getType()), derived.getUrl());
checkDifferentialBaseType(derived);
// so we have two lists - the base list, and the differential list
// the differential list is only allowed to include things that are in the base list, but
// is allowed to include them multiple times - thereby slicing them
// our approach is to walk through the base list, and see whether the differential
// says anything about them.
int baseCursor = 0;
int diffCursor = 0; // we need a diff cursor because we can only look ahead, in the bound scoped by longer paths
if (!Utilities.noString(webUrl) && !webUrl.endsWith("/"))
webUrl = webUrl + '/';
if (defWebRoot == null)
defWebRoot = webUrl;
derived.setSnapshot(new StructureDefinitionSnapshotComponent());
try {
checkDifferential(derived.getDifferential().getElement(), typeName(derived.getType()), derived.getUrl());
checkDifferentialBaseType(derived);
// so we have two lists - the base list, and the differential list
// the differential list is only allowed to include things that are in the base list, but
// is allowed to include them multiple times - thereby slicing them
// our approach is to walk through the base list, and see whether the differential
// says anything about them.
int baseCursor = 0;
int diffCursor = 0; // we need a diff cursor because we can only look ahead, in the bound scoped by longer paths
for (ElementDefinition e : derived.getDifferential().getElement())
e.clearUserData(GENERATED_IN_SNAPSHOT);
for (ElementDefinition e : derived.getDifferential().getElement())
e.clearUserData(GENERATED_IN_SNAPSHOT);
// we actually delegate the work to a subroutine so we can re-enter it with a different cursors
StructureDefinitionDifferentialComponent diff = cloneDiff(derived.getDifferential()); // we make a copy here because we're sometimes going to hack the differential while processing it. Have to migrate user data back afterwards
StructureDefinitionSnapshotComponent baseSnapshot = base.getSnapshot();
if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
String derivedType = derived.getType();
if (StructureDefinitionKind.LOGICAL.equals(derived.getKind()) && derived.getType().contains("/")) {
derivedType = derivedType.substring(derivedType.lastIndexOf("/")+1);
// we actually delegate the work to a subroutine so we can re-enter it with a different cursors
StructureDefinitionDifferentialComponent diff = cloneDiff(derived.getDifferential()); // we make a copy here because we're sometimes going to hack the differential while processing it. Have to migrate user data back afterwards
StructureDefinitionSnapshotComponent baseSnapshot = base.getSnapshot();
if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
String derivedType = derived.getType();
if (StructureDefinitionKind.LOGICAL.equals(derived.getKind()) && derived.getType().contains("/")) {
derivedType = derivedType.substring(derivedType.lastIndexOf("/")+1);
}
baseSnapshot = cloneSnapshot(baseSnapshot, base.getType(), derivedType);
}
baseSnapshot = cloneSnapshot(baseSnapshot, base.getType(), derivedType);
}
// if (derived.getId().equals("2.16.840.1.113883.10.20.22.2.1.1")) {
// debug = true;
// }
processPaths("", derived.getSnapshot(), baseSnapshot, diff, baseCursor, diffCursor, baseSnapshot.getElement().size()-1,
derived.getDifferential().hasElement() ? derived.getDifferential().getElement().size()-1 : -1, url, webUrl, derived.present(), null, null, false, base.getUrl(), null, false, null, null, new ArrayList<ElementRedirection>(), base);
checkGroupConstraints(derived);
if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
for (ElementDefinition e : diff.getElement()) {
if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) {
ElementDefinition outcome = updateURLs(url, webUrl, e.copy());
e.setUserData(GENERATED_IN_SNAPSHOT, outcome);
derived.getSnapshot().addElement(outcome);
// if (derived.getId().equals("2.16.840.1.113883.10.20.22.2.1.1")) {
// debug = true;
// }
processPaths("", derived.getSnapshot(), baseSnapshot, diff, baseCursor, diffCursor, baseSnapshot.getElement().size()-1,
derived.getDifferential().hasElement() ? derived.getDifferential().getElement().size()-1 : -1, url, webUrl, derived.present(), null, null, false, base.getUrl(), null, false, null, null, new ArrayList<ElementRedirection>(), base);
checkGroupConstraints(derived);
if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
for (ElementDefinition e : diff.getElement()) {
if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) {
ElementDefinition outcome = updateURLs(url, webUrl, e.copy());
e.setUserData(GENERATED_IN_SNAPSHOT, outcome);
derived.getSnapshot().addElement(outcome);
}
}
}
}
if (derived.getKind() != StructureDefinitionKind.LOGICAL && !derived.getSnapshot().getElementFirstRep().getType().isEmpty())
throw new Error(context.formatMessage(I18nConstants.TYPE_ON_FIRST_SNAPSHOT_ELEMENT_FOR__IN__FROM_, derived.getSnapshot().getElementFirstRep().getPath(), derived.getUrl(), base.getUrl()));
updateMaps(base, derived);
setIds(derived, false);
if (debug) {
System.out.println("Differential: ");
for (ElementDefinition ed : derived.getDifferential().getElement())
System.out.println(" "+ed.getId()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed));
System.out.println("Snapshot: ");
for (ElementDefinition ed : derived.getSnapshot().getElement())
System.out.println(" "+ed.getId()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed));
}
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
//Check that all differential elements have a corresponding snapshot element
int ce = 0;
for (ElementDefinition e : diff.getElement()) {
if (!e.hasUserData("diff-source"))
throw new Error(context.formatMessage(I18nConstants.UNXPECTED_INTERNAL_CONDITION__NO_SOURCE_ON_DIFF_ELEMENT));
else {
if (e.hasUserData(DERIVATION_EQUALS))
((Base) e.getUserData("diff-source")).setUserData(DERIVATION_EQUALS, e.getUserData(DERIVATION_EQUALS));
if (e.hasUserData(DERIVATION_POINTER))
((Base) e.getUserData("diff-source")).setUserData(DERIVATION_POINTER, e.getUserData(DERIVATION_POINTER));
}
if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) {
b.append(e.hasId() ? "id: "+e.getId() : "path: "+e.getPath());
if (e.hasId()) {
ce++;
String msg = "No match found in the generated snapshot: check that the path and definitions are legal in the differential (including order)";
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+e.getId(), msg, ValidationMessage.IssueSeverity.ERROR));
}
}
}
if (!Utilities.noString(b.toString())) {
String msg = "The profile "+derived.getUrl()+" has "+ce+" "+Utilities.pluralize("element", ce)+" in the differential ("+b.toString()+") that don't have a matching element in the snapshot: check that the path and definitions are legal in the differential (including order)";
System.out.println("Error in snapshot generation: "+msg);
if (!debug) {
if (derived.getKind() != StructureDefinitionKind.LOGICAL && !derived.getSnapshot().getElementFirstRep().getType().isEmpty())
throw new Error(context.formatMessage(I18nConstants.TYPE_ON_FIRST_SNAPSHOT_ELEMENT_FOR__IN__FROM_, derived.getSnapshot().getElementFirstRep().getPath(), derived.getUrl(), base.getUrl()));
updateMaps(base, derived);
setIds(derived, false);
if (debug) {
System.out.println("Differential: ");
for (ElementDefinition ed : derived.getDifferential().getElement())
System.out.println(" "+ed.getId()+" = "+ed.getPath()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed));
System.out.println(" "+ed.getId()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed));
System.out.println("Snapshot: ");
for (ElementDefinition ed : derived.getSnapshot().getElement())
System.out.println(" "+ed.getId()+" = "+ed.getPath()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed));
System.out.println(" "+ed.getId()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed));
}
if (exception)
throw new DefinitionException(msg);
else
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url, msg, ValidationMessage.IssueSeverity.ERROR));
}
// hack around a problem in R4 definitions (somewhere?)
for (ElementDefinition ed : derived.getSnapshot().getElement()) {
for (ElementDefinitionMappingComponent mm : ed.getMapping()) {
if (mm.hasMap()) {
mm.setMap(mm.getMap().trim());
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
//Check that all differential elements have a corresponding snapshot element
int ce = 0;
for (ElementDefinition e : diff.getElement()) {
if (!e.hasUserData("diff-source"))
throw new Error(context.formatMessage(I18nConstants.UNXPECTED_INTERNAL_CONDITION__NO_SOURCE_ON_DIFF_ELEMENT));
else {
if (e.hasUserData(DERIVATION_EQUALS))
((Base) e.getUserData("diff-source")).setUserData(DERIVATION_EQUALS, e.getUserData(DERIVATION_EQUALS));
if (e.hasUserData(DERIVATION_POINTER))
((Base) e.getUserData("diff-source")).setUserData(DERIVATION_POINTER, e.getUserData(DERIVATION_POINTER));
}
}
for (ElementDefinitionConstraintComponent s : ed.getConstraint()) {
if (s.hasSource()) {
String ref = s.getSource();
if (!Utilities.isAbsoluteUrl(ref)) {
if (ref.contains(".")) {
s.setSource("http://hl7.org/fhir/StructureDefinition/"+ref.substring(0, ref.indexOf("."))+"#"+ref);
} else {
s.setSource("http://hl7.org/fhir/StructureDefinition/"+ref);
}
}
}
}
}
if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
for (ElementDefinition ed : derived.getSnapshot().getElement()) {
if (!ed.hasBase()) {
ed.getBase().setPath(ed.getPath()).setMin(ed.getMin()).setMax(ed.getMax());
}
}
}
// last, check for wrong profiles or target profiles
for (ElementDefinition ed : derived.getSnapshot().getElement()) {
for (TypeRefComponent t : ed.getType()) {
for (UriType u : t.getProfile()) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, u.getValue());
if (sd == null) {
if (xver != null && xver.matchingUrl(u.getValue()) && xver.status(u.getValue()) == XVerExtensionStatus.Valid) {
sd = xver.makeDefinition(u.getValue());
}
if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) {
b.append(e.hasId() ? "id: "+e.getId() : "path: "+e.getPath());
ce++;
if (e.hasId()) {
String msg = "No match found in the generated snapshot: check that the path and definitions are legal in the differential (including order)";
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+e.getId(), msg, ValidationMessage.IssueSeverity.ERROR));
}
if (sd == null) {
if (messages != null) {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), "The type of profile "+u.getValue()+" cannot be checked as the profile is not known", IssueSeverity.WARNING));
}
}
if (!Utilities.noString(b.toString())) {
String msg = "The profile "+derived.getUrl()+" has "+ce+" "+Utilities.pluralize("element", ce)+" in the differential ("+b.toString()+") that don't have a matching element in the snapshot: check that the path and definitions are legal in the differential (including order)";
System.out.println("Error in snapshot generation: "+msg);
if (!debug) {
System.out.println("Differential: ");
for (ElementDefinition ed : derived.getDifferential().getElement())
System.out.println(" "+ed.getId()+" = "+ed.getPath()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed));
System.out.println("Snapshot: ");
for (ElementDefinition ed : derived.getSnapshot().getElement())
System.out.println(" "+ed.getId()+" = "+ed.getPath()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed));
}
if (exception)
throw new DefinitionException(msg);
else
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url, msg, ValidationMessage.IssueSeverity.ERROR));
}
// hack around a problem in R4 definitions (somewhere?)
for (ElementDefinition ed : derived.getSnapshot().getElement()) {
for (ElementDefinitionMappingComponent mm : ed.getMapping()) {
if (mm.hasMap()) {
mm.setMap(mm.getMap().trim());
}
}
for (ElementDefinitionConstraintComponent s : ed.getConstraint()) {
if (s.hasSource()) {
String ref = s.getSource();
if (!Utilities.isAbsoluteUrl(ref)) {
if (ref.contains(".")) {
s.setSource("http://hl7.org/fhir/StructureDefinition/"+ref.substring(0, ref.indexOf("."))+"#"+ref);
} else {
s.setSource("http://hl7.org/fhir/StructureDefinition/"+ref);
}
}
}
}
}
if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
for (ElementDefinition ed : derived.getSnapshot().getElement()) {
if (!ed.hasBase()) {
ed.getBase().setPath(ed.getPath()).setMin(ed.getMin()).setMax(ed.getMax());
}
}
}
// last, check for wrong profiles or target profiles
for (ElementDefinition ed : derived.getSnapshot().getElement()) {
for (TypeRefComponent t : ed.getType()) {
for (UriType u : t.getProfile()) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, u.getValue());
if (sd == null) {
if (xver != null && xver.matchingUrl(u.getValue()) && xver.status(u.getValue()) == XVerExtensionStatus.Valid) {
sd = xver.makeDefinition(u.getValue());
}
}
} else {
String wt = t.getWorkingCode();
if (ed.getPath().equals("Bundle.entry.response.outcome")) {
wt = "OperationOutcome";
}
if (!sd.getType().equals(wt)) {
boolean ok = isCompatibleType(wt, sd);
if (!ok) {
String smsg = "The profile "+u.getValue()+" has type "+sd.getType()+" which is not consistent with the stated type "+wt;
if (exception)
throw new DefinitionException(smsg);
else
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), smsg, IssueSeverity.ERROR));
if (sd == null) {
if (messages != null) {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), "The type of profile "+u.getValue()+" cannot be checked as the profile is not known", IssueSeverity.WARNING));
}
} else {
String wt = t.getWorkingCode();
if (ed.getPath().equals("Bundle.entry.response.outcome")) {
wt = "OperationOutcome";
}
if (!sd.getType().equals(wt)) {
boolean ok = isCompatibleType(wt, sd);
if (!ok) {
String smsg = "The profile "+u.getValue()+" has type "+sd.getType()+" which is not consistent with the stated type "+wt;
if (exception)
throw new DefinitionException(smsg);
else
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), smsg, IssueSeverity.ERROR));
}
}
}
}
}
}
} catch (Exception e) {
// if we had an exception generating the snapshot, make sure we don't leave any half generated snapshot behind
derived.setSnapshot(null);
derived.clearUserData("profileutils.snapshot.generating");
throw e;
}
} catch (Exception e) {
// if we had an exception generating the snapshot, make sure we don't leave any half generated snapshot behind
derived.setSnapshot(null);
} finally {
derived.clearUserData("profileutils.snapshot.generating");
throw e;
snapshotStack.remove(derived.getUrl());
}
derived.clearUserData("profileutils.snapshot.generating");
}
public void checkDifferentialBaseType(StructureDefinition derived) throws Error {
@ -1076,6 +1091,7 @@ public class ProfileUtilities extends TranslatingUtilities {
ElementDefinition outcome = updateURLs(url, webUrl, currentBase.copy());
outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc));
updateFromBase(outcome, currentBase);
updateConstraintSources(outcome, srcSD.getUrl());
markDerived(outcome);
if (resultPathBase == null)
resultPathBase = outcome.getPath();
@ -1131,7 +1147,7 @@ public class ProfileUtilities extends TranslatingUtilities {
processPaths(indent+" ", result, base, differential, nbc, start, nbl-1, diffCursor-1, url, webUrl, profileName, tgt.getElement().getPath(), outcome.getPath(), trimDifferential, contextName, resultPathBase, false, null, null, redirectorStack(redirector, outcome, cpath), srcSD);
}
} else {
StructureDefinition dt = outcome.getType().size() > 1 ? context.fetchTypeDefinition("Element") : getProfileForDataType(outcome.getType().get(0));
StructureDefinition dt = outcome.getType().size() > 1 ? context.fetchTypeDefinition("Element") : getProfileForDataType(outcome.getType().get(0), webUrl);
if (dt == null) {
throw new DefinitionException(context.formatMessage(I18nConstants.UNKNOWN_TYPE__AT_, outcome.getType().get(0), cpath));
}
@ -1152,7 +1168,15 @@ public class ProfileUtilities extends TranslatingUtilities {
if (diffMatches.get(0).hasType() && "Reference".equals(diffMatches.get(0).getType().get(0).getWorkingCode()) && !isValidType(diffMatches.get(0).getType().get(0), currentBase)) {
throw new DefinitionException(context.formatMessage(I18nConstants.VALIDATION_VAL_ILLEGAL_TYPE_CONSTRAINT, url, diffMatches.get(0).getPath(), diffMatches.get(0).getType().get(0), currentBase.typeSummary()));
}
if (diffMatches.get(0).hasType() && diffMatches.get(0).getType().size() == 1 && diffMatches.get(0).getType().get(0).hasProfile() && !"Reference".equals(diffMatches.get(0).getType().get(0).getWorkingCode())) {
String id = diffMatches.get(0).getId();
String lid = tail(id);
if (lid.contains("/")) {
// the template comes from the snapshot of the base
generateIds(result.getElement(), url, srcSD.getType());
String baseId = id.substring(0, id.length()-lid.length()) + lid.substring(0, lid.indexOf("/")); // this is wrong if there's more than one reslice (todo: one thing at a time)
template = getById(result.getElement(), baseId);
} else if (diffMatches.get(0).hasType() && diffMatches.get(0).getType().size() == 1 && diffMatches.get(0).getType().get(0).hasProfile() && !"Reference".equals(diffMatches.get(0).getType().get(0).getWorkingCode())) {
CanonicalType p = diffMatches.get(0).getType().get(0).getProfile().get(0);
StructureDefinition sd = context.fetchResource(StructureDefinition.class, p.getValue());
if (sd == null && xver != null && xver.matchingUrl(p.getValue())) {
@ -1166,7 +1190,7 @@ public class ProfileUtilities extends TranslatingUtilities {
}
}
if (sd != null) {
if (!isMatchingType(sd, diffMatches.get(0).getType())) {
if (!isMatchingType(sd, diffMatches.get(0).getType(), p.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT))) {
throw new DefinitionException(context.formatMessage(I18nConstants.VALIDATION_VAL_PROFILE_WRONGTYPE2, sd.getUrl(), diffMatches.get(0).getPath(), sd.getType(), p.getValue(), diffMatches.get(0).getType().get(0).getWorkingCode()));
}
if (isGenerating(sd)) {
@ -1223,7 +1247,6 @@ public class ProfileUtilities extends TranslatingUtilities {
}
}
updateFromDefinition(outcome, diffMatches.get(0), profileName, trimDifferential, url, srcSD);
// diffMatches.get(0).setUserData(GENERATED_IN_SNAPSHOT, outcome); // because of updateFromDefinition isn't called
removeStatusExtensions(outcome);
// if (outcome.getPath().endsWith("[x]") && outcome.getType().size() == 1 && !outcome.getType().get(0).getCode().equals("*") && !diffMatches.get(0).hasSlicing()) // if the base profile allows multiple types, but the profile only allows one, rename it
// outcome.setPath(outcome.getPath().substring(0, outcome.getPath().length()-3)+Utilities.capitalize(outcome.getType().get(0).getCode()));
@ -1285,7 +1308,7 @@ public class ProfileUtilities extends TranslatingUtilities {
processPaths(indent+" ", result, base, differential, nbc, start - 1, nbl-1, diffCursor - 1, url, webUrl, profileName, tgt.getElement().getPath(), diffMatches.get(0).getPath(), trimDifferential, contextName, resultPathBase, false, null, null, redirectorStack(redirector, outcome, cpath), srcSD);
}
} else {
StructureDefinition dt = outcome.getType().size() == 1 ? getProfileForDataType(outcome.getType().get(0)) : getProfileForDataType("Element");
StructureDefinition dt = outcome.getType().size() == 1 ? getProfileForDataType(outcome.getType().get(0), webUrl) : getProfileForDataType("Element");
if (dt == null)
throw new DefinitionException(context.formatMessage(I18nConstants._HAS_CHILDREN__FOR_TYPE__IN_PROFILE__BUT_CANT_FIND_TYPE, diffMatches.get(0).getPath(), differential.getElement().get(diffCursor).getPath(), typeCode(outcome.getType()), profileName));
contextName = dt.getUrl();
@ -1496,7 +1519,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (baseHasChildren(base, currentBase)) { // not a new type here
throw new Error("This situation is not yet handled (constrain slicing to 1..1 and fix base slice for inline structure - please report issue to grahame@fhir.org along with a test case that reproduces this error (@ "+cpath+" | "+currentBase.getPath()+")");
} else {
StructureDefinition dt = getTypeForElement(differential, diffCursor, profileName, diffMatches, outcome);
StructureDefinition dt = getTypeForElement(differential, diffCursor, profileName, diffMatches, outcome, webUrl);
contextName = dt.getUrl();
diffCursor++;
start = diffCursor;
@ -1562,7 +1585,7 @@ public class ProfileUtilities extends TranslatingUtilities {
processPaths(indent+" ", result, base, differential, baseCursor+1, diffCursor, baseLimit, diffLimit, url, webUrl, profileName, contextPathSrc, contextPathDst, trimDifferential, contextName, resultPathBase, false, null, null, redirector, srcSD);
baseCursor = indexOfFirstNonChild(base, currentBase, baseCursor, baseLimit);
} else {
StructureDefinition dt = getTypeForElement(differential, diffCursor, profileName, diffMatches, outcome);
StructureDefinition dt = getTypeForElement(differential, diffCursor, profileName, diffMatches, outcome, webUrl);
contextName = dt.getUrl();
int start = diffCursor;
while (differential.getElement().size() > diffCursor && pathStartsWith(differential.getElement().get(diffCursor).getPath(), cpath+"."))
@ -1763,7 +1786,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (base.getElement().get(baseCursor).getType().size() != 1) {
throw new Error(context.formatMessage(I18nConstants.DIFFERENTIAL_WALKS_INTO____BUT_THE_BASE_DOES_NOT_AND_THERE_IS_NOT_A_SINGLE_FIXED_TYPE_THE_TYPE_IS__THIS_IS_NOT_HANDLED_YET, cpath, diffMatches.get(0).toString(), base.getElement().get(baseCursor).typeSummary()));
}
StructureDefinition dt = getProfileForDataType(base.getElement().get(baseCursor).getType().get(0));
StructureDefinition dt = getProfileForDataType(base.getElement().get(baseCursor).getType().get(0), webUrl);
if (dt == null) {
throw new DefinitionException(context.formatMessage(I18nConstants.UNKNOWN_TYPE__AT_, outcome.getType().get(0), diffMatches.get(0).getPath()));
}
@ -1879,7 +1902,7 @@ public class ProfileUtilities extends TranslatingUtilities {
diffCursor - 1, url, webUrl, profileName+pathTail(diffMatches, 0), base.getElement().get(0).getPath(), base.getElement().get(0).getPath(), trimDifferential, contextName, resultPathBase, false, null, null, redirector, srcSD);
} else {
StructureDefinition dt = getProfileForDataType(outcome.getType().get(0));
StructureDefinition dt = getProfileForDataType(outcome.getType().get(0), webUrl);
// if (t.getCode().equals("Extension") && t.hasProfile() && !t.getProfile().contains(":")) {
// lloydfix dt =
// }
@ -1913,6 +1936,24 @@ public class ProfileUtilities extends TranslatingUtilities {
return res;
}
private ElementDefinition getById(List<ElementDefinition> list, String baseId) {
for (ElementDefinition t : list) {
if (baseId.equals(t.getId())) {
return t;
}
}
return null;
}
private void updateConstraintSources(ElementDefinition ed, String url) {
for (ElementDefinitionConstraintComponent c : ed.getConstraint()) {
if (!c.hasSource()) {
c.setSource(url);
}
}
}
private Set<String> getListOfTypes(ElementDefinition e) {
Set<String> result = new HashSet<>();
for (TypeRefComponent t : e.getType()) {
@ -1922,7 +1963,7 @@ public class ProfileUtilities extends TranslatingUtilities {
}
public StructureDefinition getTypeForElement(StructureDefinitionDifferentialComponent differential, int diffCursor, String profileName,
List<ElementDefinition> diffMatches, ElementDefinition outcome) {
List<ElementDefinition> diffMatches, ElementDefinition outcome, String webUrl) {
if (outcome.getType().size() == 0) {
throw new DefinitionException(context.formatMessage(I18nConstants._HAS_NO_CHILDREN__AND_NO_TYPES_IN_PROFILE_, diffMatches.get(0).getPath(), differential.getElement().get(diffCursor).getPath(), profileName));
}
@ -1932,7 +1973,7 @@ public class ProfileUtilities extends TranslatingUtilities {
throw new DefinitionException(context.formatMessage(I18nConstants._HAS_CHILDREN__AND_MULTIPLE_TYPES__IN_PROFILE_, diffMatches.get(0).getPath(), differential.getElement().get(diffCursor).getPath(), typeCode(outcome.getType()), profileName));
}
}
StructureDefinition dt = getProfileForDataType(outcome.getType().get(0));
StructureDefinition dt = getProfileForDataType(outcome.getType().get(0), webUrl);
if (dt == null)
throw new DefinitionException(context.formatMessage(I18nConstants.UNKNOWN_TYPE__AT_, outcome.getType().get(0), diffMatches.get(0).getPath()));
return dt;
@ -1948,21 +1989,43 @@ public class ProfileUtilities extends TranslatingUtilities {
return b.toString();
}
private boolean isMatchingType(StructureDefinition sd, List<TypeRefComponent> types) {
private boolean isMatchingType(StructureDefinition sd, List<TypeRefComponent> types, String inner) {
while (sd != null) {
for (TypeRefComponent tr : types) {
if (sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition") && sd.getType().equals(tr.getCode())) {
return true;
}
if (sd.getUrl().equals(tr.getCode())) {
if (inner == null && sd.getUrl().equals(tr.getCode())) {
return true;
}
if (inner != null) {
ElementDefinition ed = null;
for (ElementDefinition t : sd.getSnapshot().getElement()) {
if (inner.equals(t.getId())) {
ed = t;
}
}
if (ed != null) {
return isMatchingType(ed.getType(), types);
}
}
}
sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
}
return false;
}
private boolean isMatchingType(List<TypeRefComponent> test, List<TypeRefComponent> desired) {
for (TypeRefComponent t : test) {
for (TypeRefComponent d : desired) {
if (t.getCode().equals(d.getCode())) {
return true;
}
}
}
return false;
}
private boolean isValidType(TypeRefComponent t, ElementDefinition base) {
for (TypeRefComponent tr : base.getType()) {
if (tr.getCode().equals(t.getCode())) {
@ -2065,6 +2128,8 @@ public class ProfileUtilities extends TranslatingUtilities {
private void removeStatusExtensions(ElementDefinition outcome) {
outcome.removeExtension(ToolingExtensions.EXT_FMM_LEVEL);
outcome.removeExtension(ToolingExtensions.EXT_FMM_SUPPORT);
outcome.removeExtension(ToolingExtensions.EXT_FMM_DERIVED);
outcome.removeExtension(ToolingExtensions.EXT_STANDARDS_STATUS);
outcome.removeExtension(ToolingExtensions.EXT_NORMATIVE_VERSION);
outcome.removeExtension(ToolingExtensions.EXT_WORKGROUP);
@ -2380,10 +2445,16 @@ public class ProfileUtilities extends TranslatingUtilities {
return s;
}
private StructureDefinition getProfileForDataType(TypeRefComponent type) {
private StructureDefinition getProfileForDataType(TypeRefComponent type, String webUrl) {
StructureDefinition sd = null;
if (type.hasProfile()) {
sd = context.fetchResource(StructureDefinition.class, type.getProfile().get(0).getValue());
if (sd == null) {
if (xver != null && xver.matchingUrl(type.getProfile().get(0).getValue()) && xver.status(type.getProfile().get(0).getValue()) == XVerExtensionStatus.Valid) {
sd = xver.makeDefinition(type.getProfile().get(0).getValue());
generateSnapshot(context.fetchTypeDefinition("Extension"), sd, sd.getUrl(), webUrl, sd.getName());
}
}
if (sd == null)
System.out.println("Failed to find referenced profile: " + type.getProfile());
}
@ -2453,24 +2524,27 @@ public class ProfileUtilities extends TranslatingUtilities {
if (webUrl != null) {
// also, must touch up the markdown
if (element.hasDefinition())
element.setDefinition(processRelativeUrls(element.getDefinition(), webUrl));
element.setDefinition(processRelativeUrls(element.getDefinition(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, null, false));
if (element.hasComment())
element.setComment(processRelativeUrls(element.getComment(), webUrl));
element.setComment(processRelativeUrls(element.getComment(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, null, false));
if (element.hasRequirements())
element.setRequirements(processRelativeUrls(element.getRequirements(), webUrl));
element.setRequirements(processRelativeUrls(element.getRequirements(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, null, false));
if (element.hasMeaningWhenMissing())
element.setMeaningWhenMissing(processRelativeUrls(element.getMeaningWhenMissing(), webUrl));
element.setMeaningWhenMissing(processRelativeUrls(element.getMeaningWhenMissing(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, null, false));
}
}
return element;
}
private String processRelativeUrls(String markdown, String webUrl) {
public static String processRelativeUrls(String markdown, String webUrl, String basePath, List<String> resourceNames, Set<String> baseFilenames, Set<String> localFilenames, boolean processRelatives) {
if (markdown == null) {
return "";
}
StringBuilder b = new StringBuilder();
int i = 0;
while (i < markdown.length()) {
if (i < markdown.length()-3 && markdown.substring(i, i+2).equals("](")) {
int j = i + 2;
int j = i + 2;
while (j < markdown.length() && markdown.charAt(j) != ')')
j++;
if (j < markdown.length()) {
@ -2486,13 +2560,21 @@ public class ProfileUtilities extends TranslatingUtilities {
// This code is trying to guess which relative references are actually to the
// base specification.
//
if (isLikelySourceURLReference(url)) {
if (isLikelySourceURLReference(url, resourceNames, baseFilenames, localFilenames)) {
b.append("](");
b.append(baseSpecUrl());
b.append(basePath);
i = i + 1;
} else {
b.append("](");
b.append(webUrl);
// disabled 7-Dec 2021 GDG - we don't want to fool with relative URLs at all?
// re-enabled 11-Feb 2022 GDG - we do want to do this. At least, $assemble in davinci-dtr, where the markdown comes from the SDC IG, and an SDC local reference must be changed to point to SDC. in this case, it's called when generating snapshots
// added processRelatives parameter to deal with this (well, to try)
if (processRelatives && webUrl != null && !issLocalFileName(url, localFilenames)) {
// System.out.println("Making "+url+" relative to '"+webUrl+"'");
b.append(webUrl);
} else {
// System.out.println("Not making "+url+" relative to '"+webUrl+"'");
}
i = i + 1;
}
} else
@ -2508,12 +2590,62 @@ public class ProfileUtilities extends TranslatingUtilities {
}
private boolean isLikelySourceURLReference(String url) {
public static boolean issLocalFileName(String url, Set<String> localFilenames) {
if (localFilenames != null) {
for (String n : localFilenames) {
if (url.startsWith(n.toLowerCase())) {
return true;
}
}
}
return false;
}
public static boolean isLikelySourceURLReference(String url, List<String> resourceNames, Set<String> baseFilenames, Set<String> localFilenames) {
if (resourceNames != null) {
for (String n : resourceNames) {
if (url.startsWith(n.toLowerCase()+".html")) {
return true;
}
if (url.startsWith(n.toLowerCase()+"-definitions.html")) {
return true;
}
}
}
if (localFilenames != null) {
for (String n : localFilenames) {
if (url.startsWith(n.toLowerCase())) {
return false;
}
}
}
if (baseFilenames != null) {
for (String n : baseFilenames) {
if (url.startsWith(n.toLowerCase())) {
return true;
}
}
}
return
url.startsWith("extensibility.html") ||
url.startsWith("terminologies.html") ||
url.startsWith("observation.html") ||
url.startsWith("codesystem.html") ||
url.startsWith("fhirpath.html") ||
url.startsWith("datatypes.html") ||
(url.startsWith("extension-") || url.contains(".html")) ||
url.startsWith("operations.html") ||
url.startsWith("resource.html") ||
url.startsWith("elementdefinition.html") ||
url.startsWith("element-definitions.html") ||
url.startsWith("snomedct.html") ||
url.startsWith("loinc.html") ||
url.startsWith("http.html") ||
url.startsWith("references") ||
url.startsWith("narrative.html") ||
url.startsWith("search.html") ||
url.startsWith("patient-operation-match.html") ||
(url.startsWith("extension-") && url.contains(".html")) ||
url.startsWith("resource-definitions.html");
}
@ -2533,6 +2665,9 @@ public class ProfileUtilities extends TranslatingUtilities {
if (VersionUtilities.isR2Ver(context.getVersion())) {
return "http://hl7.org/fhir/DSTU2/";
}
if (VersionUtilities.isR4BVer(context.getVersion())) {
return "http://hl7.org/fhir/2021Mar/";
}
return "";
}
@ -2716,7 +2851,11 @@ public class ProfileUtilities extends TranslatingUtilities {
profile = source.getType().size() == 1 && source.getTypeFirstRep().hasProfile() ? context.fetchResource(StructureDefinition.class, source.getTypeFirstRep().getProfile().get(0).getValue()) : null;
if (profile != null) {
ElementDefinition e = profile.getSnapshot().getElement().get(0);
base.setDefinition(e.getDefinition());
String webroot = profile.getUserString("webroot");
if (e.hasDefinition()) {
base.setDefinition(processRelativeUrls(e.getDefinition(), webroot, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, null, true));
}
base.setShort(e.getShort());
if (e.hasCommentElement())
base.setCommentElement(e.getCommentElement());
@ -3445,7 +3584,7 @@ public class ProfileUtilities extends TranslatingUtilities {
List<TypeRefComponent> types = e.getType();
if (!e.hasType()) {
if (root) { // we'll use base instead of types then
StructureDefinition bsd = context.fetchResource(StructureDefinition.class, profile.getBaseDefinition());
StructureDefinition bsd = profile == null ? null : context.fetchResource(StructureDefinition.class, profile.getBaseDefinition());
if (bsd != null) {
if (bsd.hasUserData("path")) {
c.getPieces().add(gen.new Piece(Utilities.isAbsoluteUrl(bsd.getUserString("path")) ? bsd.getUserString("path") : imagePath +bsd.getUserString("path"), bsd.getName(), null));
@ -3527,7 +3666,7 @@ public class ProfileUtilities extends TranslatingUtilities {
c.addPiece(checkForNoChange(tl, gen.new Piece(null,", ", null)));
}
ref = pkp.getLinkForProfile(profile, p.getValue());
ref = pkp == null ? null : pkp.getLinkForProfile(profile, p.getValue());
if (ref != null) {
String[] parts = ref.split("\\|");
if (parts[0].startsWith("http:") || parts[0].startsWith("https:")) {
@ -3608,7 +3747,7 @@ public class ProfileUtilities extends TranslatingUtilities {
}
private String codeForAggregation(AggregationMode a) {
public static String codeForAggregation(AggregationMode a) {
switch (a) {
case BUNDLED : return "b";
case CONTAINED : return "c";
@ -3617,7 +3756,7 @@ public class ProfileUtilities extends TranslatingUtilities {
}
}
private String hintForAggregation(AggregationMode a) {
public static String hintForAggregation(AggregationMode a) {
if (a != null)
return a.getDefinition();
else
@ -3626,7 +3765,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private String checkPrepend(String corePath, String path) {
if (pkp.prependLinks() && !(path.startsWith("http:") || path.startsWith("https:")))
if (pkp != null && pkp.prependLinks() && !(path.startsWith("http:") || path.startsWith("https:")))
return corePath+path;
else
return path;
@ -3998,7 +4137,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (logicalModel && element.hasRepresentation(PropertyRepresentation.XMLATTR))
sName = "@"+sName;
Cell nc = genElementNameCell(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName);
genElementCells(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, nc, mustSupport);
genElementCells(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, nc, mustSupport, true);
if (element.hasSlicing()) {
if (standardExtensionSlicing(element)) {
used.used = true; // doesn't matter whether we have a type, we're used if we're setting up slicing ... element.hasType() && element.getType().get(0).hasProfile();
@ -4126,7 +4265,7 @@ public class ProfileUtilities extends TranslatingUtilities {
public List<Cell> genElementCells(HierarchicalTableGenerator gen, ElementDefinition element, String profileBaseFileName, boolean snapshot, String corePath,
String imagePath, boolean root, boolean logicalModel, boolean allInvariants, StructureDefinition profile, Row typesRow, Row row, boolean hasDef,
boolean ext, UnusedTracker used, String ref, String sName, Cell nameCell, boolean mustSupport) throws IOException {
boolean ext, UnusedTracker used, String ref, String sName, Cell nameCell, boolean mustSupport, boolean allowSubRows) throws IOException {
List<Cell> res = new ArrayList<>();
Cell gc = gen.new Cell();
row.getCells().add(gc);
@ -4152,7 +4291,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (extDefn == null) {
res.add(genCardinality(gen, element, row, hasDef, used, null));
res.add(addCell(row, gen.new Cell(null, null, "?gen-e1? "+element.getType().get(0).getProfile(), null, null)));
res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, profile == null ? "" : profile.getUrl(), eurl, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport));
res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, profile == null ? "" : profile.getUrl(), eurl, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows));
} else {
String name = urltail(eurl);
nameCell.getPieces().get(0).setText(name);
@ -4165,7 +4304,7 @@ public class ProfileUtilities extends TranslatingUtilities {
else // if it's complex, we just call it nothing
// genTypes(gen, row, extDefn.getSnapshot().getElement().get(0), profileBaseFileName, profile);
res.add(addCell(row, gen.new Cell(null, null, "("+translate("sd.table", "Complex")+")", null, null)));
res.add(generateDescription(gen, row, element, extDefn.getElement(), used.used, null, extDefn.getUrl(), profile, corePath, imagePath, root, logicalModel, allInvariants, valueDefn, snapshot, mustSupport));
res.add(generateDescription(gen, row, element, extDefn.getElement(), used.used, null, extDefn.getUrl(), profile, corePath, imagePath, root, logicalModel, allInvariants, valueDefn, snapshot, mustSupport, allowSubRows));
}
} else {
res.add(genCardinality(gen, element, row, hasDef, used, null));
@ -4173,7 +4312,7 @@ public class ProfileUtilities extends TranslatingUtilities {
res.add(addCell(row, gen.new Cell()));
else
res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, mustSupport));
res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport));
res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows));
}
} else {
res.add(genCardinality(gen, element, row, hasDef, used, null));
@ -4181,7 +4320,7 @@ public class ProfileUtilities extends TranslatingUtilities {
res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, mustSupport));
else
res.add(addCell(row, gen.new Cell()));
res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport));
res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows));
}
return res;
}
@ -4232,12 +4371,12 @@ public class ProfileUtilities extends TranslatingUtilities {
private boolean isBaseCondition(IdType c) {
String key = c.asStringValue();
return key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-");
return key != null && key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-");
}
private boolean isBaseConstraint(ElementDefinitionConstraintComponent con) {
String key = con.getKey();
return key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-");
return key != null && key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-");
}
private void makeChoiceRows(List<Row> subRows, ElementDefinition element, HierarchicalTableGenerator gen, String corePath, String profileBaseFileName, boolean mustSupportMode) {
@ -4487,11 +4626,11 @@ public class ProfileUtilities extends TranslatingUtilities {
&& element.getSlicing().getRules() != SlicingRules.CLOSED && element.getSlicing().getDiscriminator().size() == 1 && element.getSlicing().getDiscriminator().get(0).getPath().equals("url") && element.getSlicing().getDiscriminator().get(0).getType().equals(DiscriminatorType.VALUE);
}
private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, boolean snapshot, boolean mustSupportOnly) throws IOException, FHIRException {
return generateDescription(gen, row, definition, fallback, used, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot, mustSupportOnly);
private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows) throws IOException, FHIRException {
return generateDescription(gen, row, definition, fallback, used, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot, mustSupportOnly, allowSubRows);
}
private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, ElementDefinition valueDefn, boolean snapshot, boolean mustSupportOnly) throws IOException, FHIRException {
private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, ElementDefinition valueDefn, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows) throws IOException, FHIRException {
Cell c = gen.new Cell();
row.getCells().add(c);
@ -4507,7 +4646,7 @@ public class ProfileUtilities extends TranslatingUtilities {
}
}
if (root) {
if (profile.getAbstract()) {
if (profile != null && profile.getAbstract()) {
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
c.addPiece(gen.new Piece(null, "This is an abstract profile", null));
}
@ -4602,7 +4741,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (binding!=null && !binding.isEmpty()) {
if (!c.getPieces().isEmpty())
c.addPiece(gen.new Piece("br"));
BindingResolution br = pkp.resolveBinding(profile, binding, definition.getPath());
BindingResolution br = pkp == null ? makeNullBr(binding) : pkp.resolveBinding(profile, binding, definition.getPath());
c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, translate("sd.table", "Binding")+": ", null).addStyle("font-weight:bold")));
c.getPieces().add(checkForNoChange(binding.getValueSetElement(), gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !pkp.prependLinks() ? br.url : corePath+br.url, br.display, null)));
if (binding.hasStrength()) {
@ -4611,7 +4750,7 @@ public class ProfileUtilities extends TranslatingUtilities {
c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(null, ")", null)));
}
if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) {
br = pkp.resolveBinding(profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), definition.getPath());
br = pkp == null ? makeNullBr(binding) : pkp.resolveBinding(profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), definition.getPath());
c.addPiece(gen.new Piece("br"));
c.getPieces().add(checkForNoChange(binding, gen.new Piece(corePath+"extension-elementdefinition-maxvalueset.html", translate("sd.table", "Max Binding")+": ", "Max Value Set Extension").addStyle("font-weight:bold")));
c.getPieces().add(checkForNoChange(binding, gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !pkp.prependLinks() ? br.url : corePath+br.url, br.display, null)));
@ -4626,6 +4765,13 @@ public class ProfileUtilities extends TranslatingUtilities {
c.getPieces().add(gen.new Piece(null, ": ", null));
c.addMarkdownNoPara(PublicationHacker.fixBindingDescriptions(context, binding.getDescriptionElement()).asStringValue(), checkForNoChange(PublicationHacker.fixBindingDescriptions(context, binding.getDescriptionElement())));
}
if (binding.hasExtension(ToolingExtensions.EXT_BINDING_ADDITIONAL)) {
c.addPiece(gen.new Piece("br"));
c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, translate("sd.table", "Additional Bindings")+": ", null).addStyle("font-weight:bold")));
for (Extension ext : binding.getExtensionsByUrl(ToolingExtensions.EXT_BINDING_ADDITIONAL)) {
renderAdditionalBinding(gen, c, ext);
}
}
}
for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) {
if (!inv.hasSource() || profile == null || inv.getSource().equals(profile.getUrl()) || allInvariants) {
@ -4647,7 +4793,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (definition.hasFixed()) {
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, translate("sd.table", "Fixed Value")+": ", null).addStyle("font-weight:bold")));
if (!useTableForFixedValues || definition.getFixed().isPrimitive()) {
if (!useTableForFixedValues || !allowSubRows || definition.getFixed().isPrimitive()) {
String s = buildJson(definition.getFixed());
String link = null;
if (Utilities.isAbsoluteUrl(s))
@ -4655,7 +4801,7 @@ public class ProfileUtilities extends TranslatingUtilities {
c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(link, s, null).addStyle("color: darkgreen")));
} else {
c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "As shown", null).addStyle("color: darkgreen")));
genFixedValue(gen, row, definition.getFixed(), snapshot, false, corePath, false);
genFixedValue(gen, row, definition.getFixed(), snapshot, false, corePath, false);
}
if (isCoded(definition.getFixed()) && !hasDescription(definition.getFixed())) {
Piece p = describeCoded(gen, definition.getFixed());
@ -4665,7 +4811,7 @@ public class ProfileUtilities extends TranslatingUtilities {
} else if (definition.hasPattern()) {
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, translate("sd.table", "Required Pattern")+": ", null).addStyle("font-weight:bold")));
if (!useTableForFixedValues || definition.getPattern().isPrimitive())
if (!useTableForFixedValues || !allowSubRows || definition.getPattern().isPrimitive())
c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, buildJson(definition.getPattern()), null).addStyle("color: darkgreen")));
else {
c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, "At least the following", null).addStyle("color: darkgreen")));
@ -4705,6 +4851,30 @@ public class ProfileUtilities extends TranslatingUtilities {
return c;
}
private void renderAdditionalBinding(HierarchicalTableGenerator gen, Cell c, Extension ext) {
// <nsbp>2 <sp> purpose <sp> value-set-link ([context]) {documentation}
String purpose = ext.getExtensionString("purpose");
String valueSet = ext.getExtensionString("valueSet");
String doco = ext.getExtensionString("documentation");
//UsageContext usage = (ext.hasExtension("usage")) ? ext.getExtensionByUrl("usage").getValueUsageContext() : null;
//
// purpose: code - defines how the binding is used
// usage : UsageContext - defines the contexts in which this binding is used for it's purpose
// valueSet : canonical(ValueSet)
// documentation : markdown
// !!
// c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getKey()+": ", null).addStyle("font-weight:bold")));
// c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, gt(inv.getHumanElement()), null)));
}
private BindingResolution makeNullBr(ElementDefinitionBindingComponent binding) {
BindingResolution br = new BindingResolution();
br.url = "http://none.none/none";
br.display = "todo";
return br;
}
private ElementDefinitionBindingComponent makeUnifiedBinding(ElementDefinitionBindingComponent binding, ElementDefinition element) {
if (!element.hasUserData(DERIVATION_POINTER)) {
return binding;
@ -4739,7 +4909,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private void genFixedValue(HierarchicalTableGenerator gen, Row erow, DataType value, boolean snapshot, boolean pattern, String corePath, boolean skipnoValue) {
String ref = pkp.getLinkFor(corePath, value.fhirType());
if (ref != null) {
if (ref != null && ref.contains(".html")) {
ref = ref.substring(0, ref.indexOf(".html"))+"-definitions.html#";
} else {
ref = "?gen-fv?";
@ -4883,13 +5053,13 @@ public class ProfileUtilities extends TranslatingUtilities {
private Piece describeCoded(HierarchicalTableGenerator gen, DataType fixed) {
if (fixed instanceof Coding) {
Coding c = (Coding) fixed;
ValidationResult vr = context.validateCode(terminologyServiceOptions , c.getSystem(), c.getCode(), c.getDisplay());
ValidationResult vr = context.validateCode(terminologyServiceOptions , c.getSystem(), c.getVersion(), c.getCode(), c.getDisplay());
if (vr.getDisplay() != null)
return gen.new Piece(null, " ("+vr.getDisplay()+")", null).addStyle("color: darkgreen");
} else if (fixed instanceof CodeableConcept) {
CodeableConcept cc = (CodeableConcept) fixed;
for (Coding c : cc.getCoding()) {
ValidationResult vr = context.validateCode(terminologyServiceOptions, c.getSystem(), c.getCode(), c.getDisplay());
ValidationResult vr = context.validateCode(terminologyServiceOptions, c.getSystem(), c.getVersion(), c.getCode(), c.getDisplay());
if (vr.getDisplay() != null)
return gen.new Piece(null, " ("+vr.getDisplay()+")", null).addStyle("color: darkgreen");
}
@ -5118,7 +5288,9 @@ public class ProfileUtilities extends TranslatingUtilities {
}
private String tail(String path) {
if (path.contains("."))
if (path == null) {
return "";
} else if (path.contains("."))
return path.substring(path.lastIndexOf('.')+1);
else
return path;
@ -6634,5 +6806,14 @@ public class ProfileUtilities extends TranslatingUtilities {
public ElementDefinitionResolution resolveContentRef(StructureDefinition structure, ElementDefinition element) {
return getElementById(structure, structure.getSnapshot().getElement(), element.getContentReference());
}
public Set<String> getMasterSourceFileNames() {
return masterSourceFileNames;
}
public void setMasterSourceFileNames(Set<String> masterSourceFileNames) {
this.masterSourceFileNames = masterSourceFileNames;
}
}

View File

@ -0,0 +1,13 @@
package org.hl7.fhir.r4b.renderers;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4b.model.PrimitiveType;
public interface IMarkdownProcessor {
@SuppressWarnings("rawtypes")
public String processMarkdown(String location, PrimitiveType md) throws FHIRException;
public String processMarkdown(String location, String text) throws FHIRException;
}

View File

@ -28,7 +28,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
}
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));
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;
}