001package org.hl7.fhir.r5.renderers; 002 003import java.util.Date; 004 005import org.hl7.fhir.r5.comparison.VersionComparisonAnnotation; 006import org.hl7.fhir.r5.context.IWorkerContext; 007import org.hl7.fhir.r5.model.Base; 008import org.hl7.fhir.r5.model.Enumeration; 009import org.hl7.fhir.r5.renderers.utils.RenderingContext; 010import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; 011import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType; 012import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; 013import org.hl7.fhir.utilities.MarkDownProcessor; 014import org.hl7.fhir.utilities.StandardsStatus; 015import org.hl7.fhir.utilities.Utilities; 016import org.hl7.fhir.utilities.MarkDownProcessor.Dialect; 017import org.hl7.fhir.utilities.validation.ValidationOptions; 018import org.hl7.fhir.utilities.xhtml.NodeType; 019import org.hl7.fhir.utilities.xhtml.XhtmlNode; 020 021/** 022 * Rendering framework: 023 * 024 * * boolean render(DomainResource) : produce an HTML representation suitable for runtime / documentation, and insert it into the resource. Return true of any extensions encountered 025 * * boolean render(XhtmlNode, Resource: produce an HTML representation, and fill out the provided node with it. Return true of any extensions encountered 026 * * XhtmlNode build(DomainResource): same as render(DomainResource) but also return the XHtmlNode 027 * 028 * * String display(Base) : produce a plan text concise representation that serves to describe the resource 029 * * void display(XhtmlNode, Base) : produce a plan text concise representation that serves to describe the resource 030 * 031 * * void describe(XhtmlNode, Resource) : produce a short summary of the resource with key details presented (potentially more verbose than display, but still suitable for a single line) 032 * 033 * if not specific code for rendering a resource has been provided, and there's no liquid script to guide it, a generic rendering based onthe profile will be performed 034 * 035 * @author graha 036 * 037 */ 038public class Renderer { 039 040 protected RenderingContext context; 041 042 public Renderer(RenderingContext context) { 043 this.context = context; 044 } 045 046 public Renderer(IWorkerContext worker) { 047 this.context = new RenderingContext(worker, new MarkDownProcessor(Dialect.COMMON_MARK), ValidationOptions.defaults(), "http://hl7.org/fhir/R5", "", null, ResourceRendererMode.END_USER, GenerationRules.IG_PUBLISHER); 048 } 049 050 051 protected String formatPhrase(String theMessage, Object... theMessageArguments) { 052 return context.formatPhrase(theMessage, theMessageArguments); 053 } 054 055 public void genStandardsStatus(XhtmlNode td, StandardsStatus ss) { 056 if (ss != null) { 057 td.tx(" "); 058 XhtmlNode a = td.ah(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "versions.html#std-process"), (context.formatPhrase(RenderingContext.REND_STANDARDS, ss.toDisplay()))); 059 a.style("padding-left: 3px; padding-right: 3px; border: 1px grey solid; font-weight: bold; color: black; background-color: "+ss.getColor()); 060 a.tx(ss.getAbbrev()); 061 } 062 } 063 064 protected XhtmlNode renderStatus(Base b, XhtmlNode x) { 065 if (b == null || context.getChangeVersion() == null) { 066 return x; 067 } 068 VersionComparisonAnnotation vca = (VersionComparisonAnnotation) b.getUserData(VersionComparisonAnnotation.USER_DATA_NAME); 069 if (vca == null) { 070 return x; 071 } 072 switch (vca.getType()) { 073 case Added: 074 XhtmlNode spanOuter = x.span("border: solid 1px #dddddd; margin: 2px; padding: 2px", null); 075 XhtmlNode spanInner = spanOuter.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", (context.formatPhrase(RenderingContext.REND_SINCE_ADDED, context.getChangeVersion()))); 076 spanInner.img("icon-change-add.png", "icon"); 077 spanInner.tx(" "+context.formatPhrase(RenderingContext.REND_ADDED)); 078 return spanOuter; 079 case Changed: 080 spanOuter = x.span("border: solid 1px #dddddd; margin: 2px; padding: 2px", null); 081 spanInner = spanOuter.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", vca.getOriginal() == null ? context.formatPhrase(RenderingContext.REND_SINCE_CHANGED, context.getChangeVersion()) : context.formatPhrase(RenderingContext.REND_SINCE_CHANGED_WAS, context.getChangeVersion(), vca.getOriginal())); 082 spanInner.img("icon-change-edit.png", "icon"); 083 spanInner.tx(" "+context.formatPhrase(RenderingContext.REND_CHANGED)); 084 return spanOuter; 085 case Deleted: 086 spanOuter = x.span("border: solid 1px #dddddd; margin: 2px; padding: 2px", null); 087 spanInner = spanOuter.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", (context.formatPhrase(RenderingContext.GENERAL_REMOVED_SINCE, context.getChangeVersion()))); 088 spanInner.img("icon-change-remove.png", "icon"); 089 spanInner.tx(" "+context.formatPhrase(RenderingContext.REND_REMOVED)); 090 return spanOuter.strikethrough(); 091 default: 092 return x; 093 } 094 } 095 096 protected XhtmlNode renderStatusDiv(Base b, XhtmlNode x) { 097 if (b == null || context.getChangeVersion() == null) { 098 return x; 099 } 100 VersionComparisonAnnotation vca = (VersionComparisonAnnotation) b.getUserData(VersionComparisonAnnotation.USER_DATA_NAME); 101 if (vca == null) { 102 return x; 103 } 104 switch (vca.getType()) { 105 case Added: 106 XhtmlNode divOuter = x.div("border: solid 1px #dddddd; margin: 2px; padding: 2px"); 107 XhtmlNode spanInner = divOuter.para().style("margin: 0").span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", (context.formatPhrase(RenderingContext.REND_SINCE_ADDED, context.getChangeVersion()))); 108 spanInner.img("icon-change-add.png", "icon"); 109 spanInner.tx(" "+context.formatPhrase(RenderingContext.REND_ADDED)); 110 return divOuter; 111 case Changed: 112 divOuter = x.div("border: solid 1px #dddddd; margin: 2px; padding: 2px"); 113 spanInner = divOuter.para().style("margin: 0").span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", vca.getOriginal() == null ? context.formatPhrase(RenderingContext.REND_SINCE_CHANGED, context.getChangeVersion()) : context.formatPhrase(RenderingContext.REND_SINCE_CHANGED_WAS, context.getChangeVersion(), vca.getOriginal())); 114 spanInner.img("icon-change-edit.png", "icon"); 115 spanInner.tx(" "+context.formatPhrase(RenderingContext.REND_CHANGED)); 116 return divOuter; 117 case Deleted: 118 divOuter = x.div("border: solid 1px #dddddd; margin: 2px; padding: 2px"); 119 spanInner = divOuter.para().style("margin: 0").span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", (context.formatPhrase(RenderingContext.GENERAL_REMOVED_SINCE, context.getChangeVersion()))); 120 spanInner.img("icon-change-remove.png", "icon"); 121 spanInner.tx(" "+context.formatPhrase(RenderingContext.REND_REMOVED)); 122 return divOuter.strikethrough(); 123 default: 124 return x; 125 } 126 } 127 128 129 protected XhtmlNode renderStatusRow(Base b, XhtmlNode tbl, XhtmlNode tr) { 130 if (b == null || context.getChangeVersion() == null) { 131 return tr.td(); 132 } 133 VersionComparisonAnnotation vca = (VersionComparisonAnnotation) b.getUserData(VersionComparisonAnnotation.USER_DATA_NAME); 134 if (vca == null) { 135 return tr.td(); 136 } 137 switch (vca.getType()) { 138 case Added: 139 if (tbl.isClass("grid")) { 140 tr.style("border: solid 1px #dddddd; margin: 2px; padding: 2px"); 141 } 142 XhtmlNode td = tr.td(); 143 XhtmlNode span = td.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", (context.formatPhrase(RenderingContext.REND_ROW_SINCE, context.getChangeVersion()))); 144 span.img("icon-change-add.png", "icon"); 145 span.tx(" "+ context.formatPhrase(RenderingContext.REND_ADDED)); 146 XhtmlNode x = new XhtmlNode(NodeType.Element, "holder"); 147 x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", context.formatPhrase(RenderingContext.REND_ROW_SINCE, context.getChangeVersion())).tx(" "); 148 tr.styleCells(x); 149 return td; 150 case Changed: 151 td = tr.td(); 152 span = td.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", context.formatPhrase(RenderingContext.REND_ROW_CHANGED_SINCE_WAS, context.getChangeVersion(), vca.getOriginal())); 153 span.img("icon-change-edit.png", "icon"); 154 span.tx(" "+ context.formatPhrase(RenderingContext.REND_CHANGED)); 155 return td; 156 case Deleted: 157 tr.style("text-decoration: line-through"); 158 td = tr.td(); 159 span = td.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", context.formatPhrase(RenderingContext.GENERAL_REMOVED_SINCE, context.getChangeVersion())); 160 span.img("icon-change-remove.png", "icon"); 161 span.tx(" "+ context.formatPhrase(RenderingContext.REND_REMOVED)); 162 x = new XhtmlNode(NodeType.Element, "holder"); 163 x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px; text-decoration: none", context.formatPhrase(RenderingContext.REND_ROW_SINCE, context.getChangeVersion())).tx(" "); 164 tr.styleCells(x); 165 return td; 166 default: 167 return tr.td(); 168 } 169 } 170 171 public static void renderStatusSummary(RenderingContext context, Base base, XhtmlNode x, String version, String... metadataFields) { 172 if (base.hasUserData(VersionComparisonAnnotation.USER_DATA_NAME)) { 173 VersionComparisonAnnotation self = (VersionComparisonAnnotation) base.getUserData(VersionComparisonAnnotation.USER_DATA_NAME); 174 switch (self.getType()) { 175 case Added: 176 XhtmlNode spanInner = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", context.formatPhrase(RenderingContext.REND_SINCE_ADDED, version)); 177 spanInner.img("icon-change-add.png", "icon"); 178 spanInner.tx(" "+context.formatPhrase(RenderingContext.REND_ADDED)); 179 return; 180 case Changed: 181 if (self.getComp().noChangeOtherThanMetadata(metadataFields)) { 182 x.span("color: #eeeeee").tx("n/c"); 183 return; 184 } else { 185 spanInner = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", 186 self.getOriginal() != null ? context.formatPhrase(RenderingContext.REND_SINCE_CHANGED_WAS, version, self.getOriginal()) : context.formatPhrase(RenderingContext.REND_SINCE_CHANGED, version)); 187 spanInner.img("icon-change-edit.png", "icon"); 188 spanInner.tx(" "+context.formatPhrase(RenderingContext.REND_CHANGED)); 189 } 190 return; 191 case Deleted: 192 spanInner = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", context.formatPhrase(RenderingContext.GENERAL_REMOVED_SINCE, version)); 193 spanInner.img("icon-change-remove.png", "icon"); 194 spanInner.tx(" "+context.formatPhrase(RenderingContext.REND_REMOVED)); 195 return; 196 default: 197 x.span("color: #eeeeee").tx("n/c"); 198 return; 199 } 200 } else { 201 x.span("color: #eeeeee").tx("--"); 202 } 203 } 204 205 206 public String egt(@SuppressWarnings("rawtypes") Enumeration<? extends Enum> value) { 207 if (value == null || !value.hasPrimitiveValue()) { 208 return null; 209 } else { 210 return (value == null || !value.hasPrimitiveValue()) ? null : value.asStringValue(); 211 } 212 } 213 214 public String toStr(int value) { 215 return Integer.toString(value); 216 } 217 218 public String toStr(Date value) { 219 return value.toString(); 220 } 221}