Merge pull request #1869 from hapifhir/2025-01-gg-openehr-fixes

2025 01 gg openehr fixes
This commit is contained in:
Grahame Grieve 2025-01-04 14:55:49 +11:00 committed by GitHub
commit f2a951f500
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 1677 additions and 173 deletions

View File

@ -261,7 +261,7 @@ public class ProfilePathProcessor {
private void processSimplePathDefault(ElementDefinition currentBase, String currentBasePath, List<ElementDefinition> diffMatches, ProfilePathProcessorState cursors, MappingAssistant mapHelper) {
// ok, the differential slices the item. Let's check our pre-conditions to ensure that this is correct
if (!profileUtilities.unbounded(currentBase) && !profileUtilities.isSlicedToOneOnly(diffMatches.get(0)))
if (!profileUtilities.unbounded(currentBase) && !(profileUtilities.isSlicedToOneOnly(diffMatches.get(0)) || profileUtilities.isTypeSlicing(diffMatches.get(0))))
// you can only slice an element that doesn't repeat if the sum total of your slices is limited to 1
// (but you might do that in order to split up constraints by type)
throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ATTEMPT_TO_A_SLICE_AN_ELEMENT_THAT_DOES_NOT_REPEAT__FROM__IN_, currentBase.getPath(), currentBase.getPath(), cursors.contextName, diffMatches.get(0).getId(), profileUtilities.sliceNames(diffMatches)));
@ -614,8 +614,10 @@ public class ProfilePathProcessor {
}
}
if (firstTypeStructureDefinition != null) {
if (!profileUtilities.isMatchingType(firstTypeStructureDefinition, diffMatches.get(0).getType(), firstTypeProfile.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT))) {
throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.VALIDATION_VAL_PROFILE_WRONGTYPE2, firstTypeStructureDefinition.getUrl(), diffMatches.get(0).getPath(), firstTypeStructureDefinition.getType(), firstTypeProfile.getValue(), diffMatches.get(0).getType().get(0).getWorkingCode()));
if (!profileUtilities.isGenerating(firstTypeStructureDefinition)) { // can't do this check while generating
if (!profileUtilities.isMatchingType(firstTypeStructureDefinition, diffMatches.get(0).getType(), firstTypeProfile.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT))) {
throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.VALIDATION_VAL_PROFILE_WRONGTYPE2, firstTypeStructureDefinition.getUrl(), diffMatches.get(0).getPath(), firstTypeStructureDefinition.getType(), firstTypeProfile.getValue(), diffMatches.get(0).getType().get(0).getWorkingCode()));
}
}
if (profileUtilities.isGenerating(firstTypeStructureDefinition)) {
// this is a special case, because we're only going to access the first element, and we can rely on the fact that it's already populated.
@ -638,8 +640,13 @@ public class ProfilePathProcessor {
if (eid.equals(t.getId()))
src = t;
}
if (src == null)
throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.UNABLE_TO_FIND_ELEMENT__IN_, eid, firstTypeProfile.getValue()));
if (src == null) {
if (profileUtilities.isGenerating(firstTypeStructureDefinition)) {
System.out.println("At this time the reference to "+eid+" cannot be handled - consult Grahame Grieve");
} else if (Utilities.existsInList(currentBase.typeSummary(), "Extension", "Resource")) {
throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.UNABLE_TO_FIND_ELEMENT__IN_, eid, firstTypeProfile.getValue()));
}
}
} else {
if (firstTypeStructureDefinition.getSnapshot().getElement().isEmpty()) {
throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.SNAPSHOT_IS_EMPTY, firstTypeStructureDefinition.getVersionedUrl(), "Source for first element"));

View File

@ -843,6 +843,9 @@ public class ProfileUtilities {
String msg = "No match found for "+e.getId()+" in the generated snapshot: check that the path and definitions are legal in the differential (including order)";
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, "StructureDefinition.differential.element["+i+"]", msg, ValidationMessage.IssueSeverity.ERROR));
}
} else {
ElementDefinition sed = (ElementDefinition) e.getUserData(UserDataNames.SNAPSHOT_GENERATED_IN_SNAPSHOT);
sed.setUserData(UserDataNames.SNAPSHOT_DERIVATION_DIFF, e); // note: this means diff/snapshot are cross-linked
}
i++;
}
@ -1034,11 +1037,11 @@ public class ProfileUtilities {
}
private void checkTypeParameters(StructureDefinition base, StructureDefinition derived) {
String bt = ToolingExtensions.readStringExtension(base, ToolingExtensions.EXT_TYPE_PARAMETER);
String bt = ToolingExtensions.readStringSubExtension(base, ToolingExtensions.EXT_TYPE_PARAMETER, "type");
if (!derived.hasExtension(ToolingExtensions.EXT_TYPE_PARAMETER)) {
throw new DefinitionException(context.formatMessage(I18nConstants.SD_TYPE_PARAMETER_MISSING, base.getVersionedUrl(), bt, derived.getVersionedUrl()));
}
String dt = ToolingExtensions.readStringExtension(derived, ToolingExtensions.EXT_TYPE_PARAMETER);
String dt = ToolingExtensions.readStringSubExtension(derived, ToolingExtensions.EXT_TYPE_PARAMETER, "type");
StructureDefinition bsd = context.fetchTypeDefinition(bt);
StructureDefinition dsd = context.fetchTypeDefinition(dt);
if (bsd == null) {
@ -1101,20 +1104,39 @@ public class ProfileUtilities {
}
}
private void copyInheritedExtensions(StructureDefinition base, StructureDefinition derived, String webUrl) {
for (Extension ext : base.getExtension()) {
if (!Utilities.existsInList(ext.getUrl(), NON_INHERITED_ED_URLS) && !derived.hasExtension(ext.getUrl())) {
Extension next = ext.copy();
if (ext.hasValueMarkdownType()) {
MarkdownType md = ext.getValueMarkdownType();
md.setValue(processRelativeUrls(md.getValue(), webUrl, context.getSpecUrl(), context.getResourceNames(), masterSourceFileNames, localFileNames, false));
if (!Utilities.existsInList(ext.getUrl(), NON_INHERITED_ED_URLS)) {
String action = getExtensionAction(ext.getUrl());
if (!"ignore".equals(action)) {
boolean exists = derived.hasExtension(ext.getUrl());
if ("add".equals(action) || !exists) {
Extension next = ext.copy();
if (next.hasValueMarkdownType()) {
MarkdownType md = next.getValueMarkdownType();
md.setValue(processRelativeUrls(md.getValue(), webUrl, context.getSpecUrl(), context.getResourceNames(), masterSourceFileNames, localFileNames, false));
}
derived.getExtension().add(next);
} else if ("overwrite".equals(action)) {
Extension oext = derived.getExtensionByUrl(ext.getUrl());
Extension next = ext.copy();
if (next.hasValueMarkdownType()) {
MarkdownType md = next.getValueMarkdownType();
md.setValue(processRelativeUrls(md.getValue(), webUrl, context.getSpecUrl(), context.getResourceNames(), masterSourceFileNames, localFileNames, false));
}
oext.setValue(next.getValue());
}
}
derived.getExtension().add(next);
}
}
}
private String getExtensionAction(String url) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, url);
if (sd != null && sd.hasExtension(ToolingExtensions.EXT_SNAPSHOT_BEHAVIOR)) {
return ToolingExtensions.readStringExtension(sd, ToolingExtensions.EXT_SNAPSHOT_BEHAVIOR);
}
return "defer";
}
private void addInheritedElementsForSpecialization(StructureDefinitionSnapshotComponent snapshot, ElementDefinition focus, String type, String path, String url, String weburl) {
@ -1490,17 +1512,18 @@ public class ProfileUtilities {
}
protected boolean isMatchingType(StructureDefinition sd, List<TypeRefComponent> types, String inner) {
while (sd != null) {
StructureDefinition tsd = sd;
while (tsd != null) {
for (TypeRefComponent tr : types) {
if (sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition") && sd.getType().equals(tr.getCode())) {
if (tsd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition") && tsd.getType().equals(tr.getCode())) {
return true;
}
if (inner == null && sd.getUrl().equals(tr.getCode())) {
if (inner == null && tsd.getUrl().equals(tr.getCode())) {
return true;
}
if (inner != null) {
ElementDefinition ed = null;
for (ElementDefinition t : sd.getSnapshot().getElement()) {
for (ElementDefinition t : tsd.getSnapshot().getElement()) {
if (inner.equals(t.getId())) {
ed = t;
}
@ -1510,7 +1533,7 @@ public class ProfileUtilities {
}
}
}
sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition(), sd);
tsd = context.fetchResource(StructureDefinition.class, tsd.getBaseDefinition(), tsd);
}
return false;
}
@ -2261,6 +2284,13 @@ public class ProfileUtilities {
return (e.hasSlicing() && e.hasMaxElement() && e.getMax().equals("1"));
}
protected boolean isTypeSlicing(ElementDefinition e) {
return (e.hasSlicing() && e.getSlicing().getDiscriminator().size() == 1 &&
e.getSlicing().getDiscriminatorFirstRep().getType() == DiscriminatorType.TYPE &&
"$this".equals(e.getSlicing().getDiscriminatorFirstRep().getPath()));
}
protected ElementDefinitionSlicingComponent makeExtensionSlicing() {
ElementDefinitionSlicingComponent slice = new ElementDefinitionSlicingComponent();
slice.addDiscriminator().setPath("url").setType(DiscriminatorType.VALUE);
@ -3109,6 +3139,10 @@ public class ProfileUtilities {
if (sd != null && sd.hasExtension(ToolingExtensions.EXT_BINDING_STYLE)) {
return true;
}
if (sd != null && sd.hasExtension(ToolingExtensions.EXT_TYPE_CHARACTERISTICS) &&
"can-bind".equals(ToolingExtensions.readStringExtension(sd, ToolingExtensions.EXT_TYPE_CHARACTERISTICS))) {
return true;
}
}
return false;
}

View File

@ -292,7 +292,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
return b.toString();
}
private String lookupCode(String system, String version, String code) {
public String lookupCode(String system, String version, String code) {
if (JurisdictionUtilities.isJurisdiction(system)) {
return JurisdictionUtilities.displayJurisdiction(system+"#"+code);
}
@ -924,7 +924,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
}
/**
* this is overriden in ResourceRenderer where a better rendering is performed
* this is overridden in ResourceRenderer where a better rendering is performed
* @param status
* @param x
* @param ref
@ -1136,7 +1136,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
return s;
}
private String displayCodeSource(String system, String version) {
public String displayCodeSource(String system, String version) {
String s = displaySystem(system);
if (version != null) {
s = s + "["+describeVersion(version)+"]";
@ -1212,7 +1212,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
}
}
private String getLinkForSystem(String system, String version) {
public String getLinkForSystem(String system, String version) {
if ("http://snomed.info/sct".equals(system)) {
return "https://browser.ihtsdotools.org/";
} else if ("http://loinc.org".equals(system)) {
@ -1234,7 +1234,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
}
}
protected String getLinkForCode(String system, String version, String code) {
public String getLinkForCode(String system, String version, String code) {
if ("http://snomed.info/sct".equals(system)) {
return SnomedUtilities.getSctLink(version, code, context.getContext().getExpansionParameters());
} else if ("http://loinc.org".equals(system)) {
@ -2097,36 +2097,39 @@ public class DataRenderer extends Renderer implements CodeResolver {
if (rep.has("count"))
b.append(context.formatPhrase(RenderingContext.DATA_REND_COUNT, rep.primitiveValue("count")) + " " + " times");
if (rep.has("duration"))
b.append(context.formatPhrase(RenderingContext.DATA_REND_DURATION, rep.primitiveValue("duration")+displayTimeUnits(rep.primitiveValue("periodUnit"))) + " ");
b.append(context.formatPhrase(RenderingContext.DATA_REND_DURATION, rep.primitiveValue("duration")+displayTimeUnits(rep.primitiveValue("periodUnit"), "1".equals(rep.primitiveValue("duration")))) + " ");
if (rep.has("when")) {
String st = "";
if (rep.has("offset")) {
st = rep.primitiveValue("offset")+"min ";
}
b.append(st);
for (ResourceWrapper wh : rep.children("when")) {
b.append(displayEventCode(wh.primitiveValue()));
}
} else {
String st = "";
if (!rep.has("frequency") || (!rep.has("frequencyMax") && rep.primitiveValue("frequency").equals("1"))) {
st = context.formatPhrase(RenderingContext.DATA_REND_ONCE);
} else {
st = rep.primitiveValue("frequency");
if (rep.has("frequencyMax"))
st = st + "-"+rep.primitiveValue("frequencyMax");
}
if (rep.has("period")) {
st = st + " "+ (context.formatPhrase(RenderingContext.DATA_REND_PER))+" "+rep.primitiveValue("period");
if (rep.has("periodMax"))
st = st + "-"+rep.primitiveValue("periodMax");
st = st + " "+displayTimeUnits(rep.primitiveValue("periodUnit"));
}
b.append(st);
String st = "";
if (rep.has("offset")) {
st = rep.primitiveValue("offset")+"min ";
}
if (rep.has("boundsPeriod") && rep.child("boundsPeriod").has("end"))
b.append(context.formatPhrase(RenderingContext.DATA_REND_UNTIL, displayDateTime(rep.child("boundsPeriod").child("end"))) + " ");
if (!Utilities.noString(st)) {
b.append(st);
}
for (ResourceWrapper wh : rep.children("when")) {
b.append(displayEventCode(wh.primitiveValue()));
}
st = "";
if (!rep.has("frequency") || (!rep.has("frequencyMax") && rep.primitiveValue("frequency").equals("1"))) {
st = context.formatPhrase(RenderingContext.DATA_REND_ONCE);
} else {
st = rep.primitiveValue("frequency");
if (rep.has("frequencyMax"))
st = st + "-"+rep.primitiveValue("frequencyMax");
}
if (rep.has("period")) {
st = st + " "+ (context.formatPhrase(RenderingContext.DATA_REND_PER))+" "+rep.primitiveValue("period");
if (rep.has("periodMax")) {
st = st + "-"+rep.primitiveValue("periodMax");
}
st = st + " "+displayTimeUnits(rep.primitiveValue("periodUnit"), "1".equals(rep.primitiveValue("period")));
}
if (!Utilities.noString(st)) {
b.append(st);
}
if (rep.has("boundsPeriod") && rep.child("boundsPeriod").has("end")) {
b.append(context.formatPhrase(RenderingContext.DATA_REND_UNTIL, displayDateTime(rep.child("boundsPeriod").child("end"))) + " ");
}
}
return b.toString();
}
@ -2141,7 +2144,9 @@ public class DataRenderer extends Renderer implements CodeResolver {
}
private String displayEventCode(String when) {
switch (when) {
if (when == null)
return "??";
switch (when.toLowerCase()) {
case "c": return (context.formatPhrase(RenderingContext.DATA_REND_MEALS));
case "cd": return (context.formatPhrase(RenderingContext.DATA_REND_ATLUNCH));
case "cm": return (context.formatPhrase(RenderingContext.DATA_REND_ATBKFST));
@ -2156,22 +2161,36 @@ public class DataRenderer extends Renderer implements CodeResolver {
case "pcm": return (context.formatPhrase(RenderingContext.DATA_REND_AFTRBKFST));
case "pcv": return (context.formatPhrase(RenderingContext.DATA_REND_AFTRDINR));
case "wake": return (context.formatPhrase(RenderingContext.DATA_REND_AFTRWKNG));
default: return "?ngen-6?";
case "morn": return (context.formatPhrase(RenderingContext.DATA_REND_MORNING));
case "morn.early": return (context.formatPhrase(RenderingContext.DATA_REND_MORNING_EARLY));
case "morn.late": return (context.formatPhrase(RenderingContext.DATA_REND_MORNING_LATE));
case "noon": return (context.formatPhrase(RenderingContext.DATA_REND_NOON));
case "aft": return (context.formatPhrase(RenderingContext.DATA_REND_AFTERNOON));
case "aft.early": return (context.formatPhrase(RenderingContext.DATA_REND_AFTERNOON_EARLY));
case "aft.late": return (context.formatPhrase(RenderingContext.DATA_REND_AFTERNOON_LATE));
case "eve": return (context.formatPhrase(RenderingContext.DATA_REND_EVENING));
case "eve.early": return (context.formatPhrase(RenderingContext.DATA_REND_EVENING_EARLY));
case "eve.late": return (context.formatPhrase(RenderingContext.DATA_REND_EVENING_LATE));
case "night": return (context.formatPhrase(RenderingContext.DATA_REND_NIGHT));
case "phs": return (context.formatPhrase(RenderingContext.DATA_REND_AFTER_SLEEP));
case "imd": return (context.formatPhrase(RenderingContext.DATA_REND_IMMEDIATE));
default: return "?"+when+"?";
}
}
private String displayTimeUnits(String units) {
private String displayTimeUnits(String units, boolean singular) {
if (units == null)
return "?ngen-7?";
return "??";
switch (units) {
case "a": return "years";
case "d": return "days";
case "h": return "hours";
case "min": return "minutes";
case "mo": return "months";
case "s": return "seconds";
case "wk": return "weeks";
default: return "?ngen-8?";
case "a": return singular ? "year" : "years";
case "d": return singular ? "day" : "days";
case "h": return singular ? "hour" : "hours";
case "min": return singular ? "minute" : "minutes";
case "mo": return singular ? "month" : "months";
case "s": return singular ? "second" : "seconds";
case "wk": return singular ? "week" : "weeks";
default: return "?"+units+"?";
}
}

View File

@ -65,6 +65,18 @@ import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionMappingCompo
import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.renderers.utils.ElementTable;
import org.hl7.fhir.r5.renderers.utils.ElementTable.ElementTableGrouping;
import org.hl7.fhir.r5.renderers.utils.ElementTable.ElementTableGroupingEngine;
import org.hl7.fhir.r5.renderers.utils.ElementTable.ElementTableGroupingState;
import org.hl7.fhir.r5.renderers.utils.ElementTable.HintDrivenGroupingEngine;
import org.hl7.fhir.r5.renderers.utils.ElementTable.JsonDrivenGroupingEngine;
import org.hl7.fhir.r5.renderers.utils.ElementTable.TableElement;
import org.hl7.fhir.r5.renderers.utils.ElementTable.TableElementConstraint;
import org.hl7.fhir.r5.renderers.utils.ElementTable.TableElementConstraintType;
import org.hl7.fhir.r5.renderers.utils.ElementTable.TableElementDefinition;
import org.hl7.fhir.r5.renderers.utils.ElementTable.TableElementDefinitionType;
import org.hl7.fhir.r5.renderers.utils.ElementTable.TableGroup;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.FixedValueFormat;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules;
@ -83,6 +95,9 @@ import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.i18n.RenderingI18nContext;
import org.hl7.fhir.utilities.json.JsonException;
import org.hl7.fhir.utilities.json.model.JsonObject;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece;
@ -91,7 +106,7 @@ import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableGenerationMo
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel;
import org.hl7.fhir.utilities.xhtml.NodeType;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.hl7.fhir.utilities.xhtml.XhtmlParser;
import org.hl7.fhir.utilities.xhtml.XhtmlParser;
public class StructureDefinitionRenderer extends ResourceRenderer {
@ -372,7 +387,8 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
private final boolean ADD_REFERENCE_TO_TABLE = true;
private boolean useTableForFixedValues = true;
private String corePath;
private String corePath;
private JsonObject resourceGroupings;
public static class UnusedTracker {
private boolean used;
@ -703,6 +719,24 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
return model;
}
public TableModel initElementTable(HierarchicalTableGenerator gen, String prefix, boolean alternating, String id, boolean isActive, TableGenerationMode mode) throws IOException {
TableModel model = gen.new TableModel(id, isActive);
model.setAlternating(alternating);
if (mode == TableGenerationMode.XML) {
model.setDocoImg(HierarchicalTableGenerator.help16AsData());
} else {
model.setDocoImg(Utilities.pathURL(prefix, "help16.png"));
}
model.setDocoRef(Utilities.pathURL("https://build.fhir.org/ig/FHIR/ig-guidance", "readingIgs.html#table-views"));
// these need to be defined, but they're never going to be shown
model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatPhrase(RenderingI18nContext.GENERAL_NAME), context.formatPhrase(RenderingI18nContext.GENERAL_LOGICAL_NAME), null, 0));
model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatPhrase(RenderingI18nContext.GENERAL_DESC_CONST), context.formatPhrase(RenderingI18nContext.SD_HEAD_DESC_DESC), null, 0));
model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatPhrase(RenderingI18nContext.GENERAL_TYPE), context.formatPhrase(RenderingI18nContext.SD_GRID_HEAD_TYPE_DESC), null, 0));
return model;
}
private Row genElement(RenderingStatus status, 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, String anchorPrefix, Resource srcSD, List<Column> columns, ResourceWrapper res) throws IOException, FHIRException {
Row originalRow = slicingRow;
@ -4931,4 +4965,325 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
return "<a title=\"" + cs.present() + "\" href=\"" + Utilities.escapeXml(cs.getWebPath()) + "#" + cs.getId() + "-" + coding.getCode() + "\">" + coding.getCode() + "</a>" + (!coding.hasDisplay() ? "" : "(\"" + gt(coding.getDisplayElement()) + "\")");
}
public XhtmlNode buildElementTable(RenderingStatus status, String defFile, StructureDefinition profile, String imageFolder, boolean inlineGraphics, String profileBaseFileName, boolean snapshot, String corePath, String imagePath,
boolean logicalModel, boolean allInvariants, Set<String> outputTracker, boolean mustSupport, RenderingContext rc, String anchorPrefix, ResourceWrapper res) throws IOException {
// in order to build this, we need to know the base type of the profile
ElementTableGroupingEngine groupings = getElementTableGroupings(profile.getType());
if (groupings == null) {
XhtmlNode node = new XhtmlNode(NodeType.Element, "div");
node.para().tx("This view is not supported for this profile because it is of an unsupported type");
return node;
} else if (profile.getSnapshot().getElement().isEmpty()) {
XhtmlNode node = new XhtmlNode(NodeType.Element, "div");
node.para().tx("This view is not supported for this profile because it doesn't have a snapshot");
return node;
} else {
List<ElementTable.TableGroup> groups = new ArrayList<ElementTable.TableGroup>();
List<ElementDefinition> children = context.getProfileUtilities().getChildList(profile, profile.getSnapshot().getElementFirstRep());
buildElementTableRows(profile, groupings, groups, children);
HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, imageFolder, inlineGraphics, true, defFile, rc.getUniqueLocalPrefix());
gen.setTreelines(false);
TableModel model = initElementTable(gen, corePath, true, profile.getId()+"e", true, TableGenerationMode.XHTML);
new ElementTable(context, groups, this).build(gen, model);
try {
return gen.generate(model, imagePath, 0, outputTracker);
} catch (org.hl7.fhir.exceptions.FHIRException e) {
throw new FHIRException(context.getWorker().formatMessage(I18nConstants.ERROR_GENERATING_TABLE_FOR_PROFILE__, profile.getUrl(), e.getMessage()), e);
}
}
}
private void buildElementTableRows(StructureDefinition profile, ElementTableGroupingEngine groupings, List<TableGroup> groups, List<ElementDefinition> elements) {
for (ElementDefinition ed : elements) {
ElementTableGroupingState state = groupings.groupState(ed);
ElementTable.TableGroup group = getOrMakeGroup(groupings, groups, ed);
if (group != null) {
boolean isLeaf = isLeaf(ed);
if (isLeaf) {
if (hasAnyDiff(profile, ed) && !ed.isProhibited()) {
group.getElements().add(makeElement(profile, null, ed, true));
}
} else if (state == ElementTableGroupingState.DEFINES_GROUP) {
List<ElementDefinition> children = context.getProfileUtilities().getChildList(profile, ed);
buildElementTableRows(profile, group, children, ed);
} else if (hasAnyDiff(profile, ed) && !ed.isProhibited()) {
TableElement e = makeElement(profile, null, ed, false);
group.getElements().add(e);
List<ElementDefinition> children = context.getProfileUtilities().getChildList(profile, ed);
buildElementTableRows(profile, e, children);
}
} else if (!ed.isProhibited()) {
// walk the children without creating anything. Will only do anything in a logical model
List<ElementDefinition> children = context.getProfileUtilities().getChildList(profile, ed);
buildElementTableRows(profile, groupings, groups, children);
}
}
}
private void buildElementTableRows(StructureDefinition profile, ElementTable.TableGroup group, List<ElementDefinition> elements, ElementDefinition parent) {
for (ElementDefinition ed : elements) {
if (hasAnyDiff(profile, ed) && !ed.isProhibited()) {
boolean isLeaf = isLeaf(ed);
if (isLeaf) {
if (useParent(ed) ) {
group.getElements().add(makeElement(profile, parent, ed, true));
} else {
group.getElements().add(makeElement(profile, null, ed, true));
}
} else {
List<ElementDefinition> children = context.getProfileUtilities().getChildList(profile, ed);
buildElementTableRows(profile, group, children, ed);
}
}
}
}
private boolean useParent(ElementDefinition ed) {
for (TypeRefComponent t : ed.getType()) {
StructureDefinition sd = context.getContext().fetchTypeDefinition(t.getWorkingCode());
if (sd != null && sd.hasExtension(ToolingExtensions.EXT_PROFILE_VIEW_HINT)) {
for (Extension ext : sd.getExtensionsByUrl(ToolingExtensions.EXT_PROFILE_VIEW_HINT)) {
if (ext.hasValue()) {
String v = ext.getValue().primitiveValue();
if ("element-view-defns-parent".equals(v)) {
return true;
}
}
}
}
}
return true;
}
private void buildElementTableRows(StructureDefinition profile, TableElement parent, List<ElementDefinition> elements) {
for (ElementDefinition ed : elements) {
if (hasAnyDiff(profile, ed) && !ed.isProhibited()) {
boolean isLeaf = isLeaf(ed);
if (isLeaf) {
parent.getChildElements().add(makeElement(profile, null, ed, true));
} else {
TableElement e = makeElement(profile, null, ed, false);
parent.getChildElements().add(e);
List<ElementDefinition> children = context.getProfileUtilities().getChildList(profile, ed);
buildElementTableRows(profile, e, children);
}
}
}
}
private boolean isLeaf(ElementDefinition element) {
for (TypeRefComponent t : element.getType()) {
if (context.getContextUtilities().isDatatype(t.getWorkingCode()) && !Utilities.existsInList(t.getWorkingCode(), "Element", "BackboneElement")) {
return true;
}
StructureDefinition sd = context.getContext().fetchTypeDefinition(t.getWorkingCode());
if (sd != null && sd.hasExtension(ToolingExtensions.EXT_PROFILE_VIEW_HINT)) {
for (Extension ext : sd.getExtensionsByUrl(ToolingExtensions.EXT_PROFILE_VIEW_HINT)) {
if (ext.hasValue()) {
String v = ext.getValue().primitiveValue();
if ("element-view-as-leaf".equals(v)) {
return true;
}
}
}
}
}
return false;
}
private boolean hasAnyDiff(StructureDefinition profile, ElementDefinition e) {
if (e.hasUserData(UserDataNames.SNAPSHOT_DERIVATION_DIFF)) {
ElementDefinition diff = (ElementDefinition) e.getUserData(UserDataNames.SNAPSHOT_DERIVATION_DIFF);
return hasDefinitionalMaterial(diff);
}
for (ElementDefinition child : context.getProfileUtilities().getChildList(profile, e.getPath(), e.getId(), false, false)) {
if (hasAnyDiff(profile, child)) {
return true;
}
}
return false;
}
private boolean hasDefinitionalMaterial(ElementDefinition diff) {
return diff.hasShort() || diff.hasDefinition() || diff.hasComment() || diff.hasRequirements() ||
diff.hasBinding() || diff.hasFixed() || diff.hasPattern() || diff.hasMin() || diff.hasMax() ||
diff.hasMinValue() || diff.hasMaxValue() || diff.hasConstraint() || diff.hasType() || diff.hasMaxLength();
}
private ElementTableGroupingEngine getElementTableGroupings(String type) throws JsonException, IOException {
StructureDefinition sd = context.getContext().fetchTypeDefinition(type);
if (sd == null) {
return null;
}
if (sd.hasExtension(ToolingExtensions.EXT_PROFILE_VIEW_HINT) && "element-view-ready".equals(ToolingExtensions.readStringExtension(sd, ToolingExtensions.EXT_PROFILE_VIEW_HINT))) {
return new HintDrivenGroupingEngine(context.getProfileUtilities().getChildList(sd, sd.getSnapshot().getElementFirstRep()));
}
if (resourceGroupings == null) {
byte[] cnt = context.getContext().getBinaryForKey("resource-groupings.json");
if (cnt != null) {
resourceGroupings = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(cnt, true);
}
}
if (resourceGroupings != null && resourceGroupings.has(type)) {
return new JsonDrivenGroupingEngine(resourceGroupings.getJsonArray(type));
} else {
return null;
}
}
private TableGroup getOrMakeGroup(ElementTableGroupingEngine groupings, List<TableGroup> groups, ElementDefinition element) {
ElementTableGrouping grouping = groupings.getGroup(element);
if (grouping == null) {
return null;
}
for (TableGroup g : groups) {
if (g.getDefinition().getKey() == grouping.getKey()) {
return g;
}
}
TableGroup g = new TableGroup(groups.size()+1, grouping);
groups.add(g);
return g;
}
private TableElement makeElement(StructureDefinition profile, ElementDefinition parentDefn, ElementDefinition snapDefn, boolean lookAtChildren) {
ElementDefinition working = parentDefn != null ? parentDefn : snapDefn;
ElementDefinition diffDefn = (ElementDefinition) snapDefn.getUserData(UserDataNames.SNAPSHOT_DERIVATION_DIFF);
ElementDefinition diffParentDefn = parentDefn == null ? null : (ElementDefinition) parentDefn.getUserData(UserDataNames.SNAPSHOT_DERIVATION_DIFF);
ElementDefinition workingDiff = diffParentDefn != null ? diffParentDefn : diffDefn;
TableElement e = new TableElement(working.getPath(), working.getShort(), Integer.toString(working.getMin()), working.getMax());
if (snapDefn.getType().size() > 1) {
List<String> list = new ArrayList<String>();
for (TypeRefComponent tr : snapDefn.getType()) {
StructureDefinition sd = context.getContext().fetchTypeDefinition(tr.getWorkingCode());
list.add(sd == null ? tr.getWorkingCode() : sd.present());
}
e.setType("Choice", null, "Choice of "+CommaSeparatedStringBuilder.join(",", list), "icon_choice.gif");
e.getConstraints().add(TableElementConstraint.makeTypes(TableElementConstraintType.CHOICE, null, snapDefn.getType()));
} else {
StructureDefinition sd = context.getContext().fetchTypeDefinition(snapDefn.getTypeFirstRep().getWorkingCode());
if (sd == null) {
e.setType(snapDefn.getTypeFirstRep().getWorkingCode(), null, "Unknown Type", "icon_element.gif");
} else if (Utilities.existsInList(sd.getType(), "Element", "BackboneElement")) {
e.setType("Group", sd.getWebPath(), sd.present(), chooseIcon(profile, snapDefn, snapDefn.getTypeFirstRep()));
} else {
e.setType(sd.present(), sd.getWebPath(), sd.present(), chooseIcon(profile, snapDefn, snapDefn.getTypeFirstRep()));
}
if (snapDefn.getTypeFirstRep().hasProfile()) {
e.getConstraints().add(TableElementConstraint.makeList(TableElementConstraintType.PROFILE, null, snapDefn.getTypeFirstRep().getProfile()));
}
if (snapDefn.getTypeFirstRep().hasTargetProfile()) {
e.getConstraints().add(TableElementConstraint.makeList(TableElementConstraintType.TARGET, null, snapDefn.getTypeFirstRep().getTargetProfile()));
}
}
if (working.hasDefinition()) {
e.getDefinitions().add(new TableElementDefinition(TableElementDefinitionType.DEFINITION, working.getDefinition()));
} else if (snapDefn.hasDefinition()) {
e.getDefinitions().add(new TableElementDefinition(TableElementDefinitionType.DEFINITION, snapDefn.getDefinition()));
}
if (workingDiff != null && workingDiff.hasComment()) {
e.getDefinitions().add(new TableElementDefinition(TableElementDefinitionType.COMMENT, workingDiff.getComment()));
} else if (diffDefn != null && diffDefn.hasComment()) {
e.getDefinitions().add(new TableElementDefinition(TableElementDefinitionType.COMMENT, diffDefn.getComment()));
}
if (workingDiff != null && workingDiff.hasRequirements()) {
e.getDefinitions().add(new TableElementDefinition(TableElementDefinitionType.REQUIREMENTS, workingDiff.getRequirements()));
} else if (diffDefn != null && diffDefn.hasRequirements()) {
e.getDefinitions().add(new TableElementDefinition(TableElementDefinitionType.REQUIREMENTS, diffDefn.getRequirements()));
}
checkValueDomainConstraints(snapDefn, diffDefn, null, e, false);
if (lookAtChildren) {
List<ElementDefinition> children = context.getProfileUtilities().getChildList(profile, snapDefn);
for (ElementDefinition child : children) {
checkValueDomainConstraints(child, (ElementDefinition) child.getUserData(UserDataNames.SNAPSHOT_DERIVATION_DIFF), child.getName(), e, true);
}
}
return e;
}
public void checkValueDomainConstraints(ElementDefinition defn, ElementDefinition diffDefn, String path, TableElement e, boolean cardinality) {
if (cardinality) {
if (defn.getBase().getMin() != defn.getMin() || !defn.getBase().getMax().equals(defn.getMax())) {
e.getConstraints().add(TableElementConstraint.makeRange(TableElementConstraintType.CARDINALITY, path, defn.getMinElement(), defn.getMaxElement()));
}
}
// ok, now we collect constraints on the value domain, which maybe in sub-elements, though at some point we give up
if (defn.hasFixed()) {
e.getConstraints().add(TableElementConstraint.makeValue(TableElementConstraintType.FIXED, path, defn.getFixed()));
}
if (defn.hasPattern()) {
e.getConstraints().add(TableElementConstraint.makeValue(TableElementConstraintType.PATTERN, path, defn.getPattern()));
}
if (defn.hasMinValue() || defn.hasMaxValue()) {
e.getConstraints().add(TableElementConstraint.makeRange(TableElementConstraintType.RANGE, path, defn.getMinValue(), defn.getMaxValue()));
}
if (defn.hasMaxLength()) {
e.getConstraints().add(TableElementConstraint.makeValue(TableElementConstraintType.MAXLENGTH, path, defn.getMaxLengthElement()));
}
if (defn.hasBinding() && defn.getBinding().hasValueSet() && (!cardinality || (diffDefn != null && diffDefn.hasBinding())) && !defn.hasFixed()) {
e.getConstraints().add(TableElementConstraint.makeBinding(TableElementConstraintType.BINDING, path, defn.getBinding().getStrength(), defn.getBinding().getValueSet()));
}
}
private String chooseIcon(StructureDefinition profile, ElementDefinition element, TypeRefComponent tr) {
if (tail(element.getPath()).equals("extension") && isExtension(element)) {
if (element.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue()))
return "icon_extension_complex.png";
else
return "icon_extension_simple.png";
} else if (tail(element.getPath()).equals("modifierExtension")) {
if (element.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue()))
return "icon_modifier_extension_complex.png";
else
return "icon_modifier_extension_simple.png";
} else if (element.getType().size() == 0) {
if (profile != null && context.getWorker().getResourceNames().contains(profile.getType())) {
return "icon_resource.png";
} else if (element.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) {
return "icon-object-box.png";
} else {
return "icon_element.gif";
}
} else if (element.getType().size() > 1) {
if (allAreReference(element.getType())) {
return "icon_reference.png";
} else if (element.hasExtension(ToolingExtensions.EXT_JSON_PRIMITIVE_CHOICE)) {
return "icon_choice.gif";
} else {
return "icon_choice.gif";
}
} else if (element.getType().get(0).getWorkingCode() != null && element.getType().get(0).getWorkingCode().startsWith("@")) {
return "icon_reuse.png";
} else if (context.getContext().isPrimitiveType(element.getType().get(0).getWorkingCode())) {
if (keyRows.contains(element.getId())) {
return "icon-key.png";
} else {
return "icon_primitive.png";
}
} else if (element.getType().get(0).hasTarget()) {
return "icon_reference.png";
} else if (Utilities.existsInList(element.getType().get(0).getWorkingCode(), "Element", "BackboneElement")) {
return "icon_group.png";
} else if (Utilities.existsInList(element.getType().get(0).getWorkingCode(), "Base", "Element", "BackboneElement")) {
return "icon_element.gif";
} else if (context.getContext().isDataType(element.getType().get(0).getWorkingCode())) {
return "icon_datatype.gif";
} else if (element.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) {
return "icon-object-box.png";
} else {
return "icon_resource.png";
}
}
}

View File

@ -0,0 +1,854 @@
package org.hl7.fhir.r5.renderers.utils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.CodeableConcept;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.DataType;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r5.model.Enumerations.BindingStrength;
import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.Quantity;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.renderers.DataRenderer;
import org.hl7.fhir.r5.renderers.Renderer.RenderingStatus;
import org.hl7.fhir.r5.renderers.utils.ElementTable.HintDrivenGroupingEngine;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.json.model.JsonArray;
import org.hl7.fhir.utilities.json.model.JsonObject;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel;
import org.hl7.fhir.utilities.xhtml.NodeType;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
public class ElementTable {
public static class ElementTableGrouping {
private long key;
private String name;
private int priority;
public ElementTableGrouping(long key, String name, int priority) {
this.key = key;
this.name = name;
this.priority = priority;
}
public String getName() {
return name;
}
public int getPriority() {
return priority;
}
public long getKey() {
return key;
}
}
public enum ElementTableGroupingState {
UNKNOWN, DEFINES_GROUP, IN_GROUP
}
public static abstract class ElementTableGroupingEngine {
public abstract ElementTableGroupingState groupState(ElementDefinition ed);
public abstract ElementTableGrouping getGroup(ElementDefinition ed);
protected boolean nameMatches(String name, List<String> strings) {
for (String s : strings) {
if (nameMatches(name, s)) {
return true;
}
}
return false;
}
public boolean nameMatches(String name, String test) {
if (test.equals(name)) {
return true;
}
if (test.endsWith("[x]") && name.startsWith(test.substring(0, test.length()-3))) {
return true;
}
return false;
}
}
public static class JsonDrivenGroupingEngine extends ElementTableGroupingEngine {
private JsonArray groups;
public JsonDrivenGroupingEngine(JsonArray groups) {
super();
this.groups = groups;
}
public ElementTableGroupingState groupState(ElementDefinition ed) {
String name = ed.getName();
for (JsonObject o : groups.asJsonObjects() ) {
if (nameMatches(name, o.getStrings("elements"))) {
return ElementTableGroupingState.IN_GROUP;
}
}
for (JsonObject o : groups.asJsonObjects() ) {
if (o.asBoolean("all")) {
return ElementTableGroupingState.IN_GROUP;
}
}
return ElementTableGroupingState.UNKNOWN;
}
public ElementTableGrouping getGroup(ElementDefinition ed) {
String name = ed.getName();
int c = 0;
for (JsonObject o : groups.asJsonObjects() ) {
c++;
if (nameMatches(name, o.getStrings("elements"))) {
return new ElementTableGrouping(c, o.asString("name"), groups.size() - c);
}
}
c = 0;
for (JsonObject o : groups.asJsonObjects() ) {
c++;
if (o.asBoolean("all")) {
return new ElementTableGrouping(c, o.asString("name"), groups.size() - c);
}
}
return null;
}
}
public static class HintDrivenGroupingEngine extends ElementTableGroupingEngine {
private List<ElementDefinition> list;
public HintDrivenGroupingEngine(List<ElementDefinition> list) {
super();
this.list = list;
}
public ElementTableGroupingState groupState(ElementDefinition ed) {
if (ed.hasExtension(ToolingExtensions.EXT_PROFILE_VIEW_HINT)) {
List<Extension> exl = ed.getExtensionsByUrl(ToolingExtensions.EXT_PROFILE_VIEW_HINT);
for (Extension ex : exl) {
if ("element-view-group".equals(ex.getExtensionString("name")) ) {
return ElementTableGroupingState.DEFINES_GROUP;
}
}
}
return ElementTableGroupingState.UNKNOWN;
}
public ElementTableGrouping getGroup(ElementDefinition ed) {
if (ed.hasExtension(ToolingExtensions.EXT_PROFILE_VIEW_HINT)) {
String n = null;
int order = 0;
List<Extension> exl = ed.getExtensionsByUrl(ToolingExtensions.EXT_PROFILE_VIEW_HINT);
for (Extension ex : exl) {
if ("element-view-group".equals(ex.getExtensionString("name")) ) {
n = ex.getExtensionString("value");
}
if ("element-view-order".equals(ex.getExtensionString("name")) ) {
order = ToolingExtensions.readIntegerExtension(ex, "value", 0);
}
}
if (n != null) {
return new ElementTableGrouping(ed.getName().hashCode(), n, order);
}
}
return null;
}
}
public static enum TableElementDefinitionType {
DEFINITION, COMMENT, REQUIREMENTS;
}
public static class TableElementDefinition {
private TableElementDefinitionType type;
private String markdown;
public TableElementDefinition(TableElementDefinitionType type, String markdown) {
super();
this.type = type;
this.markdown = markdown;
}
public TableElementDefinitionType getType() {
return type;
}
public String getMarkdown() {
return markdown;
}
}
public enum TableElementConstraintType {
CHOICE, PROFILE, TARGET, BINDING, RANGE, FIXED, PATTERN, MAXLENGTH, CARDINALITY;
}
public static class TableElementConstraint {
private TableElementConstraintType type;
private DataType value;
private DataType value2;
private String path;
private BindingStrength strength;
private String valueSet;
private List<TypeRefComponent> types;
private List<CanonicalType> list;
public static TableElementConstraint makeValue(TableElementConstraintType type, String path, DataType value) {
TableElementConstraint self = new TableElementConstraint();
self.type = type;
self.path = path;
self.value = value;
return self;
}
public static TableElementConstraint makeRange(TableElementConstraintType type, String path, DataType value, DataType value2) {
TableElementConstraint self = new TableElementConstraint();
self.type = type;
self.path = path;
self.value = value;
self.value2 = value2;
return self;
}
public static TableElementConstraint makeBinding(TableElementConstraintType type, String path, BindingStrength strength, String valueSet) {
TableElementConstraint self = new TableElementConstraint();
self.type = type;
self.path = path;
self.strength = strength;
self.valueSet = valueSet;
return self;
}
public static TableElementConstraint makeTypes(TableElementConstraintType type, String path, List<TypeRefComponent> types) {
TableElementConstraint self = new TableElementConstraint();
self.type = type;
self.path = path;
self.types = types;
return self;
}
public static TableElementConstraint makeList(TableElementConstraintType type, String path, List<CanonicalType> list) {
TableElementConstraint self = new TableElementConstraint();
self.type = type;
self.path = path;
self.list = list;
return self;
}
// private BindingStrength strength;
// private ValueSet vs;
// private List<String> profiles;
// private List<String> targetProfiles;
// private DataType fixed;
// private DataType pattern;
// private DataType minValue;
// private DataType maxValue;
}
public class ConstraintsSorter implements Comparator<TableElementConstraint> {
@Override
public int compare(TableElementConstraint o1, TableElementConstraint o2) {
int r = StringUtils.compare(o1.path, o2.path);
return r == 0 ? o1.type.compareTo(o2.type) : r;
}
}
public static class TableElementInvariant {
private String level;
private String human;
private String fhirPath;
private String other;
private String otherFormat;
}
public static class TableElement {
// column 1
private String path;
private String name;
private String min;
private String max;
private String typeName;
private String typeIcon;
private String typeLink;
private String typeHint;
// column 2
private List<TableElementDefinition> definitions = new ArrayList<>();
// column 3
private List<TableElementConstraint> constraints = new ArrayList<>();
private List<TableElementInvariant> invariants = new ArrayList<>();
private List<TableElement> childElements = new ArrayList<ElementTable.TableElement>();
public TableElement(String path, String name, String min, String max) {
super();
this.path = path;
this.name = name;
this.min = min;
this.max = max;
}
public String getPath() {
return path;
}
public String getName() {
return name;
}
public String getMin() {
return min;
}
public String getMax() {
return max;
}
public String getTypeName() {
return typeName;
}
public String getTypeIcon() {
return typeIcon;
}
public String getTypeLink() {
return typeLink;
}
public String getTypeHint() {
return typeHint;
}
public List<TableElementDefinition> getDefinitions() {
return definitions;
}
public List<TableElementConstraint> getConstraints() {
return constraints;
}
public List<TableElementInvariant> getInvariants() {
return invariants;
}
public List<TableElement> getChildElements() {
return childElements;
}
public TableElement setPath(String path) {
this.path = path;
return this;
}
public TableElement setName(String name) {
this.name = name;
return this;
}
public TableElement setMin(String min) {
this.min = min;
return this;
}
public TableElement setMax(String max) {
this.max = max;
return this;
}
public void setType(String name, String link, String hint, String icon) {
this.typeName = name;
this.typeIcon = icon;
this.typeLink = link;
this.typeHint = hint;
}
}
public static class TableGroup {
private String name;
private String documentation;
private boolean buildIfEmpty;
private String emptyNote;
private List<TableElement> elements = new ArrayList<ElementTable.TableElement>();
private int priorty;
private int counter;
private ElementTableGrouping definition;
public TableGroup(int counter, ElementTableGrouping definition) {
super();
this.counter = counter;
this.definition = definition;
name = definition.getName();
priorty = definition.getPriority();
buildIfEmpty = false;
}
public String getName() {
return name;
}
public String getDocumentation() {
return documentation;
}
public boolean isBuildIfEmpty() {
return buildIfEmpty;
}
public String getEmptyNote() {
return emptyNote;
}
public List<TableElement> getElements() {
return elements;
}
public int getPriorty() {
return priorty;
}
public int getCounter() {
return counter;
}
public ElementTableGrouping getDefinition() {
return definition;
}
}
public static class TableGroupSorter implements Comparator<TableGroup> {
@Override
public int compare(TableGroup o1, TableGroup o2) {
if (o1.priorty == o2.priorty) {
return Integer.compare(o1.counter, o2.counter);
} else {
return Integer.compare(o2.priorty, o1.priorty); // priorty sorts backwards
}
}
}
private RenderingContext context;
private List<TableGroup> groups;
private DataRenderer dr;
public ElementTable(RenderingContext context, List<TableGroup> groups, DataRenderer dr) {
this.context = context;
this.groups = groups;
this.dr = dr;
}
public void build(HierarchicalTableGenerator gen, TableModel table) throws FHIRFormatError, DefinitionException, IOException {
Collections.sort(groups, new TableGroupSorter());
table.setBorder(true);
table.setShowHeadings(false);
for (TableGroup grp : groups) {
if (grp.getElements().size() > 0 || grp.buildIfEmpty) {
renderGroup(gen, table, grp);
}
}
}
private void renderGroup(HierarchicalTableGenerator gen, TableModel table, TableGroup grp) throws FHIRFormatError, DefinitionException, IOException {
Row row = gen.new Row();
table.getRows().add(row);
Cell cell = gen.new Cell(null, null, grp.getName(), null, null);
row.getCells().add(cell);
cell.span(3);
row.setColor("#dfdfdf");
cell.addStyle("vertical-align: middle");
cell.addStyle("font-weight: bold");
cell.addStyle("font-size: 14px");
cell.addStyle("padding-top: 10px");
cell.addStyle("padding-bottom: 10px");
boolean first = true;
for (TableElement e : grp.elements) {
renderElement(gen, row.getSubRows(), e, first);
first = false;
}
}
private void renderElement(HierarchicalTableGenerator gen, List<Row> rows, TableElement e, boolean first) throws FHIRFormatError, DefinitionException, IOException {
Row row = gen.new Row();
rows.add(row);
if (!first) {
row.setTopLine("silver");
}
renderElementIdentity(gen, row, e);
renderElementDefinition(gen, row, e);
renderElementConstraints(gen, row, e);
// if (e.invariants.size() > 0) {
// tr.style("border-bottom: none");
// tr = table.tr();
// tr.style("border-top: none");
// tr.style("border-left: black 1px solid");
// tr.style("border-right: black 1px solid");
// renderElementInvariants(tr.td().colspan(3), e);
// }
// tr.style("border-bottom: silver 1px solid");
for (TableElement child : e.getChildElements()) {
renderElement(gen, row.getSubRows(), child, false);
}
}
public void renderElementIdentity(HierarchicalTableGenerator gen, Row row, TableElement e) {
Cell cell = gen.new Cell();
cell.addCellStyle("min-width: 220px");
row.getCells().add(cell);
cell.setInnerTable(true);
cell.addText(e.getName()).addStyle("font-weight: bold");
cell.addPiece(gen.new Piece("br"));
if ("1".equals(e.min) && "1".equals(e.max)) {
cell.addText("Required");
} else if ("0".equals(e.min) && "*".equals(e.max)) {
cell.addText("Optional, Repeating");
} else if ("0".equals(e.min) && "1".equals(e.max)) {
cell.addText("Optional");
} else if ("1".equals(e.min) && "*".equals(e.max)) {
cell.addText("Repeating");
} else {
cell.addText("Cardinality: "+e.min+".."+e.max);
}
cell.addPiece(gen.new Piece("br"));
cell.addImg(e.getTypeIcon(), e.getTypeHint(), e.getTypeLink());
cell.addPiece(gen.new Piece(e.getTypeLink(), " "+e.getTypeName(), e.getTypeHint()));
}
public void renderElementConstraints(HierarchicalTableGenerator gen, Row row, TableElement e) throws FHIRFormatError, DefinitionException, IOException {
Cell cell = gen.new Cell();
cell.addCellStyle("min-width: 300px");
row.getCells().add(cell);
XhtmlNode div = new XhtmlNode(NodeType.Element, "div");
Collections.sort(e.getConstraints(), new ConstraintsSorter());
boolean first = true;
for (TableElementConstraint c : e.getConstraints()) {
if (first) first= false; else div.br();
switch (c.type) {
case BINDING:
renderBindingConstraint(div, c);
break;
case CHOICE:
renderChoiceConstraint(div, c);
break;
case FIXED:
renderValueConstraint(div, c);
break;
case MAXLENGTH:
renderValueConstraint(div, c);
break;
case PATTERN:
renderValueConstraint(div, c);
break;
case PROFILE:
renderListConstraint(div, c);
break;
case RANGE:
renderRangeConstraint(div, c);
break;
case TARGET:
renderListConstraint(div, c);
break;
case CARDINALITY:
renderCardinalityConstraint(div, c);
break;
default:
break;
}
}
cell.addXhtml(div);
}
private void renderBindingConstraint(XhtmlNode x, TableElementConstraint c) {
String name = c.path == null ? "value" : c.path;
x.code().tx(name);
ValueSet vs = context.getContext().findTxResource(ValueSet.class, c.valueSet);
if (vs == null) {
x.tx(" is bound to an unknown valueset ");
x.code().tx(c.valueSet);
} else {
x.tx(" is bound to ");
x.ah(vs.getWebPath()).tx(vs.present());
try {
ValueSetExpansionOutcome exp = context.getContext().expandVS(vs, true, false);
if (!exp.isOk()) {
x.span().attribute("title", exp.getError()).tx(" (??)");
} else if (exp.getValueset().getExpansion().getContains().size() == 1000) {
x.tx(" (>1000 codes)");
} else if (exp.getValueset().getExpansion().getContains().size() > 6) {
x.tx(" ("+exp.getValueset().getExpansion().getContains().size()+" codes)");
} else {
x.tx(". Codes:");
XhtmlNode ul = x.ul();
for (ValueSetExpansionContainsComponent cc : exp.getValueset().getExpansion().getContains()) {
String url = cc.hasSystem() && cc.hasCode() ? dr.getLinkForCode(cc.getSystem(), cc.getVersion(), cc.getCode()) : null;
var li = ul.li();
li.ahOrNot(url).tx(dr.displayCodeSource(cc.getSystem(), cc.getVersion())+": "+cc.getCode());
if (cc.hasDisplay()) {
li.tx(" \""+cc.getDisplay()+"\"");
}
}
}
} catch (Exception e) {
x.span().attribute("title", e.getMessage()).tx(" (??)");
}
}
}
private void renderChoiceConstraint(XhtmlNode x, TableElementConstraint c) {
String name = c.path == null ? "value" : c.path;
x.code().tx(name);
x.tx(" is a choice of:");
var ul = x.ul();
for (TypeRefComponent tr : c.types) {
if (tr.hasProfile()) {
for (CanonicalType ct : tr.getProfile()) {
StructureDefinition sd = context.getContext().fetchTypeDefinition(ct.primitiveValue());
if (sd == null || !sd.hasWebPath()) {
ul.li().ah(ct.primitiveValue()).tx(ct.primitiveValue());
} else {
ul.li().ah(sd.getWebPath()).tx(sd.present());
}
}
} else if (tr.hasTarget()) {
StructureDefinition sd = context.getContext().fetchTypeDefinition(tr.getWorkingCode());
var li = ul.li();
li.ah(sd.getWebPath()).tx(sd.present());
li.tx(" pointing to ");
renderTypeList(x, tr.getTargetProfile());
} else {
StructureDefinition sd = context.getContext().fetchTypeDefinition(tr.getWorkingCode());
if (sd == null || !sd.hasWebPath()) {
ul.li().code().tx(tr.getWorkingCode());
} else {
ul.li().ah(sd.getWebPath()).tx(sd.present());
}
}
}
}
private void renderValueConstraint(XhtmlNode x, TableElementConstraint c) throws FHIRFormatError, DefinitionException, IOException {
String name = c.path == null ? "value" : c.path;
x.code().tx(name);
switch (c.type) {
case FIXED:
x.tx(" is fixed to ");
break;
case MAXLENGTH:
x.tx(" is limited in length to ");
break;
case PATTERN:
if (c.value.isPrimitive()) {
x.tx(" is fixed to ");
} else {
x.tx(" must match ");
}
break;
default:
break;
}
renderValue(x, c.value);
}
public void renderValue(XhtmlNode x, DataType v) throws IOException {
if (v.isPrimitive()) {
String s = v.primitiveValue();
if (Utilities.isAbsoluteUrl(s)) {
Resource res = context.getContext().fetchResource(Resource.class, s);
if (res != null && res.hasWebPath()) {
x.ah(res.getWebPath()).tx(res instanceof CanonicalResource ? ((CanonicalResource) res).present() : s);
} else if (Utilities.isAbsoluteUrlLinkable(s)) {
x.ah(s).code().tx(s);
} else {
x.code().tx(s);
}
} else {
x.code().tx(s);
}
} else if (v instanceof Quantity) {
genQuantity(x, (Quantity) v);
} else if (v instanceof Coding) {
genCoding(x, (Coding) v);
} else if (v instanceof CodeableConcept) {
genCodeableConcept(x, (CodeableConcept) v);
} else {
dr.renderBase(new RenderingStatus(), x, v);
}
}
private void genCodeableConcept(XhtmlNode div, CodeableConcept cc) {
boolean first = true;
for (Coding c : cc.getCoding()) {
if (first) first = false; else div.tx(",");
genCoding(div, c);
}
if (cc.hasText()) {
div.code().tx(" \""+cc.getText()+"\"");
}
}
public void genQuantity(XhtmlNode div, Quantity q) {
String url = q.hasSystem() && q.hasUnit() ? dr.getLinkForCode(q.getSystem(), null, q.getCode()) : null;
var code = div.code();
if (q.hasComparator()) {
code.tx(q.getComparator().toCode());
}
code.tx(q.getValueElement().asStringValue());
code.ahOrNot(url).tx(q.getUnit());
}
public void genCoding(XhtmlNode div, Coding c) {
String url = c.hasSystem() && c.hasCode() ? dr.getLinkForCode(c.getSystem(), c.getVersion(), c.getCode()) : null;
var code = div.code();
code.ahOrNot(url).tx(dr.displayCodeSource(c.getSystem(), c.getVersion())+": "+c.getCode());
if (c.hasDisplay()) {
code.tx(" \""+c.getDisplay()+"\"");
} else {
String s = dr.lookupCode(c.getSystem(), c.getVersion(), c.getCode());
if (s != null) {
div.span().style("opacity: 0.5").tx("(\""+s+"\")");
}
}
}
private void renderRangeConstraint(XhtmlNode x, TableElementConstraint c) throws IOException {
String name = c.path == null ? "value" : c.path;
if (c.value != null && c.value2 != null) {
x.tx(name + " between ");
renderValue(x, c.value);
x.tx(" and " );
renderValue(x, c.value2);
} else if (c.value != null) {
x.tx(name + " more than ");
renderValue(x, c.value);
} else {
x.tx(name + " less than ");
renderValue(x, c.value2);
}
}
private void renderCardinalityConstraint(XhtmlNode x, TableElementConstraint c) throws IOException {
String name = c.path == null ? "value" : c.path;
x.code().tx(name);
String min = c.value.primitiveValue();
String max = c.value2.primitiveValue();
if ("1".equals(min) && "1".equals(max)) {
x.tx("is required");
} else if ("0".equals(min) && "*".equals(max)) {
x.tx("is Optional and repeats");
} else if ("0".equals(min) && "1".equals(max)) {
x.tx("is Optional");
} else if ("1".equals(min) && "*".equals(max)) {
x.tx("repeats");
} else {
x.tx("has cardinality: "+min+".."+max);
}
}
private void renderListConstraint(XhtmlNode x, TableElementConstraint c) {
String name = c.path == null ? "value" : c.path;
x.code().tx(name);
switch (c.type) {
case PROFILE:
x.tx(" must be ");
break;
case TARGET:
x.tx(" must point to ");
break;
default:
break;
}
renderTypeList(x, c.list);
}
private void renderTypeList(XhtmlNode x, List<CanonicalType> list) {
if (list.size() == 1) {
x.tx("a ");
} else {
x.tx("one of ");
}
boolean first = true;
for (int i = 0; i < list.size(); i++) {
if (first) {
first = false;
} else if (i == list.size() - 1) {
x.tx(" or " );
} else {
x.tx(", ");
}
String s = list.get(i).primitiveValue();
Resource res = context.getContext().fetchResource(Resource.class, s);
if (res != null && res.hasWebPath()) {
x.ah(res.getWebPath()).tx(res instanceof CanonicalResource ? ((CanonicalResource) res).present() : s);
} else {
x.ah(s).tx(s);
}
}
}
public void renderElementDefinition(HierarchicalTableGenerator gen, Row row, TableElement e) {
Cell cell = gen.new Cell(); row.getCells().add(cell);
for (TableElementDefinition d : e.definitions) {
if (d.getType() == TableElementDefinitionType.DEFINITION) {
cell.addMarkdown(d.getMarkdown());
} else if (d.getType() == TableElementDefinitionType.COMMENT) {
cell.addMarkdown("Comment: "+d.getMarkdown(), "font-style: italic");
}
}
}
private void renderElementInvariants(XhtmlNode td, TableElement e) {
XhtmlNode ul = td.ul();
for (TableElementInvariant t : e.invariants) {
var li = ul.li();
li.tx(t.level+": "+t.human);
li.tx(" ");
li.code().tx(t.other != null ? t.other : t.fhirPath);
}
}
}

View File

@ -160,6 +160,8 @@ public class TerminologyClientManager {
private ILoggingService logger;
private int ecosystemfailCount;
public TerminologyClientManager(ITerminologyClientFactory factory, String cacheId, ILoggingService logger) {
super();
this.factory = factory;
@ -566,29 +568,46 @@ public class TerminologyClientManager {
if (!useEcosystem) {
server = getMasterClient().getAddress();
} else {
if (usage != null) {
request = request + "&usage="+usage;
}
JsonObject json = JsonParser.parseObjectFromUrl(request);
for (JsonObject item : json.getJsonObjects("authoritative")) {
if (server == null) {
server = item.asString("url");
ecosystemfailCount = 0;
try {
if (usage != null) {
request = request + "&usage="+usage;
}
}
for (JsonObject item : json.getJsonObjects("candidates")) {
if (server == null) {
server = item.asString("url");
JsonObject json = JsonParser.parseObjectFromUrl(request);
for (JsonObject item : json.getJsonObjects("authoritative")) {
if (server == null) {
server = item.asString("url");
}
}
for (JsonObject item : json.getJsonObjects("candidates")) {
if (server == null) {
server = item.asString("url");
}
}
if (server == null) {
server = getMasterClient().getAddress();
}
if (server.contains("://tx.fhir.org")) {
try {
server = server.replace("tx.fhir.org", host());
} catch (MalformedURLException e) {
}
}
} catch (Exception e) {
// the ecosystem cal failed, so we're just going to fall back to
String msg = "Error resolving valueSet "+canonical+": "+e.getMessage();
if (!hasMessage(msg)) {
internalLog.add(new InternalLogEvent(msg, canonical, request));
}
if (logger.isDebugLogging()) {
e.printStackTrace();
}
ecosystemfailCount++;
if (ecosystemfailCount > 3) {
useEcosystem = false;
}
}
if (server == null) {
server = getMasterClient().getAddress();
}
if (server.contains("://tx.fhir.org")) {
try {
server = server.replace("tx.fhir.org", host());
} catch (MalformedURLException e) {
}
}
}
TerminologyClientContext client = serverMap.get(server);
if (client == null) {

View File

@ -82,6 +82,17 @@ public class TestDataFactory {
return FhirPublication.R5;
}
public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException {
if (rows != null && "rows".equals(name)) {
Base[] l = new Base[rows.size()];
for (int i = 0; i < rows.size(); i++) {
l[i] = BaseTableWrapper.forRow(columns, rows.get(i));
}
return l;
}
return super.getProperty(hash, name, checkValid);
}
public String cell(int row, String col) {
if (row >= 0 && row < rows.size()) {
List<String> r = rows.get(row);

View File

@ -10,10 +10,12 @@ public class ResourceSorters {
@Override
public int compare(CanonicalResource arg0, CanonicalResource arg1) {
if (arg0.getUrl() != null) {
if (arg0.getUrl() != null && arg1.getUrl() != null) {
return arg0.getUrl().compareTo(arg1.getUrl());
} else if (arg1.getUrl() != null) {
return -arg1.getUrl().compareTo(arg0.getUrl());
return -1;
} else if (arg0.getUrl() != null) {
return 1;
} else {
return 0;
}

View File

@ -278,6 +278,8 @@ public class ToolingExtensions {
public static final String EXT_ALTERNATE_CANONICAL = "http://hl7.org/fhir/StructureDefinition/alternate-canonical";
public static final String EXT_SUPPRESSED = "http://hl7.org/fhir/StructureDefinition/elementdefinition-suppress";
public static final String EXT_SUPPRESS_RESOURCE_TYPE = "http://hl7.org/fhir/tools/StructureDefinition/json-suppress-resourcetype";
public static final String EXT_PROFILE_VIEW_HINT = "http://hl7.org/fhir/tools/StructureDefinition/view-hint";
public static final String EXT_SNAPSHOT_BEHAVIOR = "http://hl7.org/fhir/tools/StructureDefinition/snapshot-behavior";
// specific extension helpers
@ -474,6 +476,7 @@ public class ToolingExtensions {
}
return null;
}
public static String readStringExtension(DomainResource c, String uri) {
Extension ex = getExtension(c, uri);
if (ex == null)
@ -495,6 +498,30 @@ public class ToolingExtensions {
return null;
}
public static String readStringSubExtension(DomainResource c, String uri, String name) {
Extension ex = getExtension(c, uri);
if (ex == null)
return null;
ex = getExtension(ex, name);
if (ex == null)
return null;
if ((ex.getValue() instanceof StringType))
return ((StringType) ex.getValue()).getValue();
if ((ex.getValue() instanceof UriType))
return ((UriType) ex.getValue()).getValue();
if (ex.getValue() instanceof CodeType)
return ((CodeType) ex.getValue()).getValue();
if (ex.getValue() instanceof IntegerType)
return ((IntegerType) ex.getValue()).asStringValue();
if (ex.getValue() instanceof Integer64Type)
return ((Integer64Type) ex.getValue()).asStringValue();
if (ex.getValue() instanceof DecimalType)
return ((DecimalType) ex.getValue()).asStringValue();
if ((ex.getValue() instanceof MarkdownType))
return ((MarkdownType) ex.getValue()).getValue();
return null;
}
@SuppressWarnings("unchecked")
public static PrimitiveType<DataType> readPrimitiveExtension(DomainResource c, String uri) {
Extension ex = getExtension(c, uri);

View File

@ -17,6 +17,7 @@ public class UserDataNames {
public static final String SNAPSHOT_DERIVATION_POINTER = "derived.pointer";
public static final String SNAPSHOT_IS_DERIVED = "derived.fact";
public static final String SNAPSHOT_GENERATED_IN_SNAPSHOT = "profileutilities.snapshot.processed";
public static final String SNAPSHOT_DERIVATION_DIFF = "profileutilities.snapshot.diffsource";
public static final String SNAPSHOT_GENERATING = "profileutils.snapshot.generating";
public static final String SNAPSHOT_GENERATED = "profileutils.snapshot.generated";
public static final String SNAPSHOT_GENERATED_MESSAGES = "profileutils.snapshot.generated.messages";

View File

@ -1163,4 +1163,6 @@ public class I18nConstants {
public static final String VS_EXP_IMPORT_FAIL_X = "VS_EXP_IMPORT_FAIL_X";
public static final String VS_EXP_FILTER_UNK = "VS_EXP_FILTER_UNK";
public static final String NO_VALID_DISPLAY_FOUND_NONE_FOR_LANG_ERR = "NO_VALID_DISPLAY_FOUND_NONE_FOR_LANG_ERR";
public static final String CONCEPTMAP_VS_NOT_A_VS = "CONCEPTMAP_VS_NOT_A_VS";
public static final String SD_DERIVATION_NO_CONCRETE = "SD_DERIVATION_NO_CONCRETE";
}

View File

@ -1,5 +1,6 @@
package org.hl7.fhir.utilities.i18n;
public class RenderingI18nContext extends I18nBase {
public static final String ACTOR_DEF_ACT = "ACTOR_DEF_ACT";
@ -931,6 +932,19 @@ public class RenderingI18nContext extends I18nBase {
public static final String MATURITY_MATURITY = "MATURITY_MATURITY";
public static final String MATURITY_STDS_STATUS = "MATURITY_STDS_STATUS";
public static final String DATA_REND_MORNING = "DATA_REND_MORNING";
public static final String DATA_REND_MORNING_EARLY = "DATA_REND_MORNING_EARLY";
public static final String DATA_REND_MORNING_LATE = "DATA_REND_MORNING_LATE";
public static final String DATA_REND_NOON = "DATA_REND_NOON";
public static final String DATA_REND_AFTERNOON = "DATA_REND_AFTERNOON";
public static final String DATA_REND_AFTERNOON_EARLY = "";
public static final String DATA_REND_AFTERNOON_LATE = "DATA_REND_AFTERNOON_LATE";
public static final String DATA_REND_EVENING = "DATA_REND_EVENING";
public static final String DATA_REND_EVENING_EARLY = "";
public static final String DATA_REND_EVENING_LATE = "DATA_REND_EVENING_LATE";
public static final String DATA_REND_NIGHT = "DATA_REND_NIGHT";
public static final String DATA_REND_AFTER_SLEEP = "DATA_REND_AFTER_SLEEP";
public static final String DATA_REND_IMMEDIATE = "DATA_REND_IMMEDIATE";
protected String getMessagesSourceFileName() {
return "rendering-phrases";

View File

@ -250,6 +250,7 @@ public class HierarchicalTableGenerator {
private List<Piece> pieces = new ArrayList<HierarchicalTableGenerator.Piece>();
private String cellStyle;
protected int span = 1;
private boolean innerTable; // if you want a multiline left cell, you have to set this to true
private TextAlignment alignment = TextAlignment.LEFT;
private String id;
@ -271,17 +272,19 @@ public class HierarchicalTableGenerator {
pieces.add(piece);
return this;
}
public Cell addMarkdown(String md) {
return addMarkdown(md, null);
}
public Cell addMarkdown(String md, String style) {
if (!Utilities.noString(md)) {
try {
Parser parser = Parser.builder().build();
Node document = parser.parse(md);
HtmlRenderer renderer = HtmlRenderer.builder().escapeHtml(true).build();
String html = renderer.render(document);
pieces.addAll(htmlToParagraphPieces(html, null));
pieces.addAll(htmlToParagraphPieces(html, style));
} catch (Exception e) {
e.printStackTrace();
}
@ -405,12 +408,22 @@ public class HierarchicalTableGenerator {
}
return piece;
}
public Cell addStyle(String style) {
for (Piece p : pieces)
p.addStyle(style);
return this;
}
public Cell addCellStyle(String style) {
if (cellStyle == null) {
cellStyle = style;
} else {
cellStyle = cellStyle+"; "+style;
}
return this;
}
public void addToHint(String text) {
for (Piece p : pieces)
p.addToHint(text);
@ -472,6 +485,25 @@ public class HierarchicalTableGenerator {
public void setId(String id) {
this.id = id;
}
public Piece addImg(String icon, String hint, String link) {
Piece p = new Piece("img");
p.attr("src", icon);
p.hint = hint;
p.reference = link;
pieces.add(p);
return p;
}
public void addXhtml(XhtmlNode div) {
Piece p = new Piece(null);
pieces.add(p);
p.children = div.childNodes;
}
public boolean isInnerTable() {
return innerTable;
}
public void setInnerTable(boolean innerTable) {
this.innerTable = innerTable;
}
}
@ -505,6 +537,8 @@ public class HierarchicalTableGenerator {
private int lineColor;
private String id;
private String opacity;
private String topLine;
private boolean partnerRow;
public List<Row> getSubRows() {
return subRows;
@ -561,6 +595,13 @@ public class HierarchicalTableGenerator {
}
return b.toString();
}
public String getTopLine() {
return topLine;
}
public void setTopLine(String topLine) {
this.topLine = topLine;
}
}
public class TableModel {
@ -571,6 +612,8 @@ public class HierarchicalTableGenerator {
private String docoRef;
private String docoImg;
private boolean alternating;
private boolean showHeadings = true;
private boolean border = false;
public TableModel(String id, boolean active) {
super();
@ -611,6 +654,18 @@ public class HierarchicalTableGenerator {
public void setAlternating(boolean alternating) {
this.alternating = alternating;
}
public boolean isShowHeadings() {
return showHeadings;
}
public void setShowHeadings(boolean showHeadings) {
this.showHeadings = showHeadings;
}
public boolean isBorder() {
return border;
}
public void setBorder(boolean border) {
this.border = border;
}
}
@ -630,6 +685,7 @@ public class HierarchicalTableGenerator {
private TableGenerationMode mode;
private RenderingI18nContext i18n;
private String uniqueLocalPrefix;
private boolean treelines = true;
public HierarchicalTableGenerator(RenderingI18nContext i18n) {
super();
@ -724,6 +780,7 @@ public class HierarchicalTableGenerator {
return model;
}
public TableModel initComparisonTable(String prefix, String id) throws IOException {
TableModel model = new TableModel(id, true);
@ -762,38 +819,45 @@ public class HierarchicalTableGenerator {
public XhtmlNode generate(TableModel model, String imagePath, int border, Set<String> outputTracker) throws IOException, FHIRException {
checkModel(model);
XhtmlNode table = new XhtmlNode(NodeType.Element, "table").setAttribute("border", Integer.toString(border)).setAttribute("cellspacing", "0").setAttribute("cellpadding", "0");
table.setAttribute("fhir", "generated-heirarchy");
if (model.isActive()) {
table.setAttribute("id", model.getId());
}
table.setAttribute("style", "border: " + border + "px #F0F0F0 solid; font-size: 11px; font-family: verdana; vertical-align: top;");
XhtmlNode tr = table.addTag("tr");
tr.setAttribute("style", "border: " + Integer.toString(1 + border) + "px #F0F0F0 solid; font-size: 11px; font-family: verdana; vertical-align: top");
XhtmlNode tc = null;
for (Title t : model.getTitles()) {
tc = renderCell(tr, t, "th", null, null, null, false, null, "white", 0, imagePath, border, outputTracker, model, null, true);
if (t.width != 0)
tc.setAttribute("style", "width: "+Integer.toString(t.width)+"px");
if (model.isBorder()) {
table.setAttribute("style", "border: 2px black solid; font-size: 11px; font-family: verdana; vertical-align: top;");
} else {
table.setAttribute("style", "border: " + border + "px #F0F0F0 solid; font-size: 11px; font-family: verdana; vertical-align: top;");
}
if (tc != null && model.getDocoRef() != null) {
XhtmlNode a = tc.addTag("span").setAttribute("style", "float: right").addTag("a").setAttribute("title", "Legend for this format").setAttribute("href", model.getDocoRef());
if (mode == TableGenerationMode.XHTML) {
a.setAttribute("no-external", "true");
if (model.isShowHeadings()) {
XhtmlNode tr = table.addTag("tr");
tr.setAttribute("fhir", "generated-heirarchy");
tr.setAttribute("style", "border: " + Integer.toString(1 + border) + "px #F0F0F0 solid; font-size: 11px; font-family: verdana; vertical-align: top");
XhtmlNode tc = null;
for (Title t : model.getTitles()) {
tc = renderCell(tr, t, "th", null, null, null, false, null, "white", 0, imagePath, border, outputTracker, model, null, true);
if (t.width != 0)
tc.setAttribute("style", "width: "+Integer.toString(t.width)+"px");
}
XhtmlNode img = a.addTag("img");
img.setAttribute("alt", "doco").setAttribute("style", "background-color: inherit").setAttribute("src", model.getDocoImg());
if (model.isActive()) {
img.setAttribute("onLoad", "fhirTableInit(this)");
if (tc != null && model.getDocoRef() != null) {
XhtmlNode a = tc.addTag("span").setAttribute("style", "float: right").addTag("a").setAttribute("title", "Legend for this format").setAttribute("href", model.getDocoRef());
if (mode == TableGenerationMode.XHTML) {
a.setAttribute("no-external", "true");
}
XhtmlNode img = a.addTag("img");
img.setAttribute("alt", "doco").setAttribute("style", "background-color: inherit").setAttribute("src", model.getDocoImg());
if (model.isActive()) {
img.setAttribute("onLoad", "fhirTableInit(this)");
}
}
}
Counter counter = new Counter();
for (Row r : model.getRows()) {
renderRow(table, r, 0, new ArrayList<Integer>(), imagePath, border, outputTracker, counter, model);
}
if (model.getDocoRef() != null) {
tr = table.addTag("tr");
tc = tr.addTag("td");
XhtmlNode tr = table.addTag("tr");
tr.setAttribute("fhir", "generated-heirarchy");
XhtmlNode tc = tr.addTag("td");
tc.setAttribute("class", "hierarchy");
tc.setAttribute("colspan", Integer.toString(model.getTitles().size()));
tc.addTag("br");
@ -807,15 +871,21 @@ public class HierarchicalTableGenerator {
private void renderRow(XhtmlNode table, Row r, int indent, List<Integer> indents, String imagePath, int border, Set<String> outputTracker, Counter counter, TableModel model) throws IOException {
counter.row();
if (!r.partnerRow) {
counter.row();
}
XhtmlNode tr = table.addTag("tr");
tr.setAttribute("fhir", "generated-heirarchy");
String color = "white";
if (r.getColor() != null)
color = r.getColor();
else if (model.isAlternating() && counter.isOdd())
color = BACKGROUND_ALT_COLOR;
tr.setAttribute("style", "border: " + border + "px #F0F0F0 solid; padding:0px; vertical-align: top; background-color: "+color+(r.getOpacity() == null ? "" : "; opacity: "+r.getOpacity()));
String lineStyle = r.getTopLine() == null ? "" : "; border-top: 1px solid "+r.getTopLine();
tr.setAttribute("style", "border: " + border + "px #F0F0F0 solid; padding:0px; vertical-align: top; background-color: "+color+(r.getOpacity() == null ? "" : "; opacity: "+r.getOpacity())+lineStyle);
if (model.isActive()) {
tr.setAttribute("id", r.getId());
}
@ -848,26 +918,34 @@ public class HierarchicalTableGenerator {
}
if (c.getId() != null) {
tc.setAttribute("id", c.getId());
}
String lineStyle = row != null && row.getTopLine() == null ? "" : "; padding-top: 3px; padding-bottom: 3px";
XhtmlNode itc = tc;
XhtmlNode itr = null;
if (c.innerTable) {
itr = tc.table("none").tr();
itc = itr.td();
}
if (indents != null) {
tc.addTag("img").setAttribute("src", srcFor(imagePath, "tbl_spacer.png")).setAttribute("style", "background-color: inherit").setAttribute("class", "hierarchy").setAttribute("alt", ".");
tc.setAttribute("style", "vertical-align: top; text-align : left; "+(c.cellStyle != null && c.cellStyle.contains("background-color") ? "" : "background-color: "+color+"; ")+"border: "+ border +"px #F0F0F0 solid; padding:0px 4px 0px 4px; white-space: nowrap; background-image: url("+imagePath+checkExists(indents, hasChildren, lineColor, outputTracker)+")"+(c.cellStyle != null ? ";"+c.cellStyle : ""));
itc.addTag("img").setAttribute("src", srcFor(imagePath, "tbl_spacer.png")).setAttribute("style", "background-color: inherit").setAttribute("class", "hierarchy").setAttribute("alt", ".");
tc.setAttribute("style", "vertical-align: top; text-align : left; "+(c.cellStyle != null && c.cellStyle.contains("background-color") ? "" : "background-color: "+color+"; ")+"border: "+ border +"px #F0F0F0 solid; padding:0px 4px 0px 4px; white-space: nowrap"+(treelines ? "; background-image: url("+imagePath+checkExists(indents, hasChildren, lineColor, outputTracker)+")" : "")+(c.cellStyle != null ? ";"+c.cellStyle : "")+lineStyle);
for (int i = 0; i < indents.size()-1; i++) {
switch (indents.get(i)) {
case NEW_REGULAR:
case NEW_SLICER:
case NEW_SLICE:
tc.addTag("img").setAttribute("src", srcFor(imagePath, "tbl_blank.png")).setAttribute("style", "background-color: inherit").setAttribute("class", "hierarchy").setAttribute("alt", ".");
itc.addTag("img").setAttribute("src", srcFor(imagePath, "tbl_blank.png")).setAttribute("style", "background-color: inherit").setAttribute("class", "hierarchy").setAttribute("alt", ".");
break;
case CONTINUE_REGULAR:
tc.addTag("img").setAttribute("src", srcFor(imagePath, "tbl_vline.png")).setAttribute("style", "background-color: inherit").setAttribute("class", "hierarchy").setAttribute("alt", ".");
itc.addTag("img").setAttribute("src", srcFor(imagePath, "tbl_vline.png")).setAttribute("style", "background-color: inherit").setAttribute("class", "hierarchy").setAttribute("alt", ".");
break;
case CONTINUE_SLICER:
tc.addTag("img").setAttribute("src", srcFor(imagePath, "tbl_vline_slicer.png")).setAttribute("style", "background-color: inherit").setAttribute("class", "hierarchy").setAttribute("alt", ".");
itc.addTag("img").setAttribute("src", srcFor(imagePath, "tbl_vline_slicer.png")).setAttribute("style", "background-color: inherit").setAttribute("class", "hierarchy").setAttribute("alt", ".");
break;
case CONTINUE_SLICE:
tc.addTag("img").setAttribute("src", srcFor(imagePath, "tbl_vline_slice.png")).setAttribute("style", "background-color: inherit").setAttribute("class", "hierarchy").setAttribute("alt", ".");
itc.addTag("img").setAttribute("src", srcFor(imagePath, "tbl_vline_slice.png")).setAttribute("style", "background-color: inherit").setAttribute("class", "hierarchy").setAttribute("alt", ".");
break;
default:
throw new Error("Unrecognized indent level: " + indents.get(i));
@ -875,7 +953,7 @@ public class HierarchicalTableGenerator {
}
if (!indents.isEmpty()) {
String sfx = table.isActive() && hasChildren ? "-open" : "";
XhtmlNode img = tc.addTag("img");
XhtmlNode img = itc.addTag("img");
switch (indents.get(indents.size()-1)) {
case NEW_REGULAR:
img.setAttribute("src", srcFor(imagePath, "tbl_vjoin_end"+sfx+".png")).setAttribute("style", "background-color: inherit").setAttribute("class", "hierarchy").setAttribute("alt", ".");
@ -903,17 +981,21 @@ public class HierarchicalTableGenerator {
}
}
}
else
tc.setAttribute("style", "vertical-align: top; text-align : left; "+(c.cellStyle != null && c.cellStyle.contains("background-color") ? "" : "background-color: "+color+"; ")+"border: "+ border +"px #F0F0F0 solid; padding:0px 4px 0px 4px"+(c.cellStyle != null ? ";"+c.cellStyle : ""));
else {
tc.setAttribute("style", "vertical-align: top; text-align : left; "+(c.cellStyle != null && c.cellStyle.contains("background-color") ? "" : "background-color: "+color+"; ")+"border: "+ border +"px #F0F0F0 solid; padding:0px 4px 0px 4px"+(c.cellStyle != null ? ";"+c.cellStyle : "")+lineStyle);
}
if (c.innerTable) {
itc = itr.td();
}
if (!Utilities.noString(icon)) {
XhtmlNode img = tc.addTag("img").setAttribute("alt", "icon").setAttribute("src", srcFor(imagePath, icon)).setAttribute("class", "hierarchy").setAttribute("style", "background-color: "+color+"; background-color: inherit").setAttribute("alt", ".");
XhtmlNode img = itc.addTag("img").setAttribute("alt", "icon").setAttribute("src", srcFor(imagePath, icon)).setAttribute("class", "hierarchy").setAttribute("style", "background-color: "+color+"; background-color: inherit").setAttribute("alt", ".");
if (hint != null)
img.setAttribute("title", hint);
tc.addText(" ");
itc.addText(" ");
}
for (Piece p : c.pieces) {
if (!Utilities.noString(p.getTag())) {
XhtmlNode tag = tc.addTag(p.getTag());
XhtmlNode tag = itc.addTag(p.getTag());
if (p.attributes != null)
for (String n : p.attributes.keySet())
tag.setAttribute(n, p.attributes.get(n));
@ -924,7 +1006,7 @@ public class HierarchicalTableGenerator {
tag.addChildNodes(p.getChildren());
}
} else if (!Utilities.noString(p.getReference())) {
XhtmlNode a = addStyle(tc.addTag("a"), p);
XhtmlNode a = addStyle(itc.addTag("a"), p);
a.setAttribute("href", prefixLocalHref(p.getReference()));
if (mode == TableGenerationMode.XHTML && suppressExternals) {
a.setAttribute("no-external", "true");
@ -943,25 +1025,25 @@ public class HierarchicalTableGenerator {
}
if (p.hasChildren()) {
tc.addChildNodes(p.getChildren());
itc.addChildNodes(p.getChildren());
}
} else {
if (!Utilities.noString(p.getHint())) {
XhtmlNode s = addStyle(tc.addTag("span"), p);
XhtmlNode s = addStyle(itc.addTag("span"), p);
s.setAttribute("title", p.getHint());
s.addText(p.getText());
} else if (p.getStyle() != null) {
XhtmlNode s = addStyle(tc.addTag("span"), p);
XhtmlNode s = addStyle(itc.addTag("span"), p);
s.addText(p.getText());
} else {
tc.addText(p.getText());
itc.addText(p.getText());
}
if (p.hasChildren()) {
tc.addChildNodes(p.getChildren());
itc.addChildNodes(p.getChildren());
}
if (p.getTagImg() != null) {
tc.tx(" ");
tc.img(p.getTagImg(), null);
itc.tx(" ");
itc.img(p.getTagImg(), null);
}
}
}
@ -983,6 +1065,15 @@ public class HierarchicalTableGenerator {
}
private String srcFor(String corePrefix, String filename) throws IOException {
if (!treelines && filename.startsWith("tbl")) {
if (filename.contains("-open")) {
filename = "tbl-open.png";
} else if (filename.contains("-closed")) {
filename = "tbl-closed.png";
} else {
filename = "tbl_blank.png";
}
}
if (inLineGraphics) {
if (files.containsKey(filename))
return files.get(filename);
@ -991,14 +1082,15 @@ public class HierarchicalTableGenerator {
byte[] bytes;
File file = ManagedFileAccess.file(Utilities.path(dest, filename));
if (!file.exists()) // because sometime this is called real early before the files exist. it will be built again later because of this
bytes = new byte[0];
bytes = new byte[0];
else
bytes = FileUtils.readFileToByteArray(file);
b.append(new String(Base64.encodeBase64(bytes)));
// files.put(filename, b.toString());
// files.put(filename, b.toString());
return b.toString();
} else
} else {
return corePrefix+filename;
}
}
public static String help16AsData() throws IOException {
@ -1072,7 +1164,7 @@ public class HierarchicalTableGenerator {
b.append(new String(encodeBase64));
files.put(filename, b.toString());
return b.toString();
} else {
} else if (treelines) {
b.append("tbl_bck");
for (Integer i : indents)
b.append(Integer.toString(i));
@ -1098,6 +1190,8 @@ public class HierarchicalTableGenerator {
}
}
return b.toString();
} else {
return "tbl_bck0.png";
}
}
@ -1177,5 +1271,13 @@ public class HierarchicalTableGenerator {
}
return "#"+uniqueLocalPrefix+"-"+url.substring(1);
}
public boolean isTreelines() {
return treelines;
}
public void setTreelines(boolean treelines) {
this.treelines = treelines;
}
}

View File

@ -1196,4 +1196,6 @@ VS_EXP_IMPORT_ERROR_X = Unable to expand excluded value set ''{0}'': {1}
VS_EXP_IMPORT_ERROR_X = Unable to expand excluded value set ''{0}'', but no error
VS_EXP_IMPORT_ERROR_TOO_COSTLY = Unable to expand excluded value set ''{0}'': too costly
VS_EXP_FILTER_UNK = ValueSet ''{0}'' Filter by property ''{1}'' and op ''{2}'' is not supported yet
CONCEPTMAP_VS_NOT_A_VS = Reference must be to a ValueSet, but found a {0} instead
SD_DERIVATION_NO_CONCRETE = {0} is labeled as an abstract type, but no concrete descendents were found (check definitions - this is usually an error unless concrete definitions are in some other package)

View File

@ -934,3 +934,16 @@ MATURITY_PUBLISHER = Publisher: {0}
MATURITY_STATUS = Status
MATURITY_MATURITY = Maturity Level: {0}
MATURITY_STDS_STATUS = Standards Status: {0}
DATA_REND_MORNING = Morning
DATA_REND_MORNING_EARLY = Early Morning
DATA_REND_MORNING_LATE = Late Morning
DATA_REND_NOON = Noon
DATA_REND_AFTERNOON = Afternoon
DATA_REND_AFTERNOON_EARLY = Early Afternoon
DATA_REND_AFTERNOON_LATE = Late Afternoon
DATA_REND_EVENING = Evening
DATA_REND_EVENING_EARLY = Early Evening
DATA_REND_EVENING_LATE = Late Evening
DATA_REND_NIGHT = Night
DATA_REND_AFTER_SLEEP = After Sleep
DATA_REND_IMMEDIATE = Immediate

View File

@ -50,7 +50,7 @@ public class TxTestsTask extends StandaloneTask{
output = Utilities.path("[tmp]");
}
boolean ok = new TxTester(new TxTester.InternalTxLoader(version), tx, false, loadExternals(externals)).setOutput(output).execute(cliContext.getModeParams(), filter);
SystemExitManager.setError(ok ? 1 : 0);
SystemExitManager.setError(ok ? 0 : 1);
SystemExitManager.finish();
}

View File

@ -526,7 +526,7 @@ public class CodeSystemValidator extends BaseValidator {
}
private void metaChecks(List<ValidationMessage> errors, Element cs, NodeStack stack, String url, String content, String caseSensitive, String hierarchyMeaning, boolean isSupplement, int count, String supp) {
if (forPublication && (url.contains("hl7.org"))) {
if (forPublication && url != null && (url.contains("hl7.org"))) {
hint(errors, "2024-03-07", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), url.contains("terminology.hl7.org") || url.contains("hl7.org/cda/stds/core"), I18nConstants.CODESYSTEM_THO_CHECK);
}
if (isSupplement) {

View File

@ -11,6 +11,7 @@ import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
@ -144,8 +145,10 @@ public class ConceptMapValidator extends BaseValidator {
}
}
}
VSReference sourceScope = readVSReference(cm, "sourceScope", "source");
VSReference targetScope = readVSReference(cm, "targetScope", "target");
BooleanHolder bh = new BooleanHolder();
VSReference sourceScope = readVSReference(errors, stack, bh, cm, "sourceScope", "source");
VSReference targetScope = readVSReference(errors, stack, bh, cm, "targetScope", "target");
ok = ok && bh.ok();
List<Element> groups = cm.getChildrenByName("group");
int ci = 0;
@ -191,7 +194,7 @@ public class ConceptMapValidator extends BaseValidator {
}
private VSReference readVSReference(Element cm, String... names) {
private VSReference readVSReference(List<ValidationMessage> errors, NodeStack stack,BooleanHolder bok, Element cm, String... names) {
for (String n : names) {
if (cm.hasChild(n, false)) {
Element e = cm.getNamedChild(n, false);
@ -206,10 +209,32 @@ public class ConceptMapValidator extends BaseValidator {
if (ref.contains("|")) {
res.url = ref.substring(0, ref.indexOf("|"));
res.version = ref.substring(ref.indexOf("|")+1);
res.vs = context.findTxResource(ValueSet.class, res.url, res.version);
Resource r = context.fetchResource(Resource.class, res.url, res.version);
if (r != null) {
if (r instanceof ValueSet) {
res.vs = (ValueSet) r;
} else {
bok.fail();
rule(errors, "2025-12-31", IssueType.INVALID, stack.getLiteralPath()+"."+n, false, I18nConstants.CONCEPTMAP_VS_NOT_A_VS, r.fhirType());
}
}
if (res.vs == null) {
res.vs = context.findTxResource(ValueSet.class, res.url, res.version);
}
} else {
res.url = ref;
res.vs = context.findTxResource(ValueSet.class, res.url);
Resource r = context.fetchResource(Resource.class, res.url);
if (r != null) {
if (r instanceof ValueSet) {
res.vs = (ValueSet) r;
} else {
bok.fail();
rule(errors, "2025-12-31", IssueType.INVALID, stack.getLiteralPath()+"."+n, false, I18nConstants.CONCEPTMAP_VS_NOT_A_VS, r.fhirType());
}
}
if (res.vs == null) {
res.vs = context.findTxResource(ValueSet.class, res.url);
}
}
return res;
}

View File

@ -162,6 +162,19 @@ public class StructureDefinitionValidator extends BaseValidator {
warning(errors, "2024-09-17", IssueType.BUSINESSRULE, stack.getLiteralPath(), !base.getExperimental() || experimental, I18nConstants.SD_BASE_EXPERIMENTAL, sd.getBaseDefinition());
}
String abstractV = src.getNamedChildValue("abstract");
if ("true".equals(abstractV)) {
String burl = src.getNamedChildValue("url");
if (burl != null) {
boolean bok = false;
for (StructureDefinition sdb : context.fetchResourcesByType(StructureDefinition.class)) {
if (burl.equals(sdb.getBaseDefinition())) {
bok = true;
}
}
warning(errors, "2024-12-31", IssueType.NOTFOUND, stack.getLiteralPath(), bok, I18nConstants.SD_DERIVATION_NO_CONCRETE, typeName);
}
}
List<Element> differentials = src.getChildrenByName("differential");
List<Element> snapshots = src.getChildrenByName("snapshot");
boolean logical = "logical".equals(src.getNamedChildValue("kind", false));
@ -684,33 +697,37 @@ public class StructureDefinitionValidator extends BaseValidator {
boolean found = false;
for (Element e : extensions) {
if (ToolingExtensions.EXT_TYPE_PARAMETER.equals(e.getNamedChildValue("url"))) {
String ename = e.getExtensionValue("name").primitiveValue();
if (name.equals(ename)) {
found = true;
String etype = e.getExtensionValue("type").primitiveValue();
for (Extension ex : sd.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_PARAMETER)) {
String tn = ex.getExtensionString("name");
if (tn != null && tn.equals(etype)) {
etype = ex.getExtensionString("type");
break;
}
}
StructureDefinition esd = context.fetchTypeDefinition(etype);
if (rule(errors, "2024-05-29", IssueType.BUSINESSRULE, stack.getLiteralPath(), esd != null, I18nConstants.SD_TYPE_PARAMETER_UNKNOWN, tc, etype)) {
StructureDefinition t = esd;
while (t != null && t != psd) {
t = context.fetchResource(StructureDefinition.class, t.getBaseDefinition());
}
ok = rule(errors, "2024-05-29", IssueType.BUSINESSRULE, stack.getLiteralPath(), t != null, I18nConstants.SD_TYPE_PARAMETER_INVALID_REF, tc, etype, tsd.getVersionedUrl(), name, type) & ok;
if (t != null) {
if (!sd.getAbstract() && esd.getAbstract()) {
warning(errors, "2024-05-29", IssueType.BUSINESSRULE, stack.getLiteralPath(), t != null, I18nConstants.SD_TYPE_PARAMETER_ABSTRACT_WARNING, tc, etype, tsd.getVersionedUrl(), name, type);
if (!e.hasExtension("name")) {
rule(errors, "2024-12-31", IssueType.BUSINESSRULE, stack.getLiteralPath(), false, I18nConstants.SD_TYPE_PARAMETER_UNKNOWN, tc, "no name");
} else {
String ename = e.getExtensionValue("name").primitiveValue();
if (name.equals(ename)) {
found = true;
String etype = e.getExtensionValue("type").primitiveValue();
for (Extension ex : sd.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_PARAMETER)) {
String tn = ex.getExtensionString("name");
if (tn != null && tn.equals(etype)) {
etype = ex.getExtensionString("type");
break;
}
}
} else {
ok = false;
}
}
StructureDefinition esd = context.fetchTypeDefinition(etype);
if (rule(errors, "2024-05-29", IssueType.BUSINESSRULE, stack.getLiteralPath(), esd != null, I18nConstants.SD_TYPE_PARAMETER_UNKNOWN, tc, etype)) {
StructureDefinition t = esd;
while (t != null && t != psd) {
t = context.fetchResource(StructureDefinition.class, t.getBaseDefinition());
}
ok = rule(errors, "2024-05-29", IssueType.BUSINESSRULE, stack.getLiteralPath(), t != null, I18nConstants.SD_TYPE_PARAMETER_INVALID_REF, tc, etype, tsd.getVersionedUrl(), name, type) & ok;
if (t != null) {
if (!sd.getAbstract() && esd.getAbstract()) {
warning(errors, "2024-05-29", IssueType.BUSINESSRULE, stack.getLiteralPath(), t != null, I18nConstants.SD_TYPE_PARAMETER_ABSTRACT_WARNING, tc, etype, tsd.getVersionedUrl(), name, type);
}
}
} else {
ok = false;
}
}
}
}
}
ok = rule(errors, "2024-05-29", IssueType.BUSINESSRULE, stack.getLiteralPath(), found, I18nConstants.SD_TYPE_PARAM_NOT_SPECIFIED, tc, tsd.getVersionedUrl(), name, path) && ok;

View File

@ -23,7 +23,7 @@
<commons_io_version>2.17.0</commons_io_version>
<guava_version>32.0.1-jre</guava_version>
<hapi_fhir_version>6.4.1</hapi_fhir_version>
<validator_test_case_version>1.7.2</validator_test_case_version>
<validator_test_case_version>1.7.3-SNAPSHOT</validator_test_case_version>
<jackson_version>2.17.0</jackson_version>
<junit_jupiter_version>5.9.2</junit_jupiter_version>
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>