mirror of
https://github.com/hapifhir/org.hl7.fhir.core.git
synced 2025-02-07 21:38:15 +00:00
Merge pull request #1869 from hapifhir/2025-01-gg-openehr-fixes
2025 01 gg openehr fixes
This commit is contained in:
commit
f2a951f500
@ -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"));
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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+"?";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
2
pom.xml
2
pom.xml
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user