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.DataType; 009import org.hl7.fhir.r5.model.Enumeration; 010import org.hl7.fhir.r5.model.Resource; 011import org.hl7.fhir.r5.renderers.utils.RenderingContext; 012import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; 013import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType; 014import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; 015import org.hl7.fhir.r5.renderers.utils.ResourceWrapper; 016import org.hl7.fhir.utilities.MarkDownProcessor; 017import org.hl7.fhir.utilities.MarkDownProcessor.Dialect; 018import org.hl7.fhir.utilities.StandardsStatus; 019import org.hl7.fhir.utilities.Utilities; 020import org.hl7.fhir.utilities.validation.ValidationOptions; 021import org.hl7.fhir.utilities.xhtml.NodeType; 022import org.hl7.fhir.utilities.xhtml.XhtmlNode; 023 024/** 025 * Rendering framework: 026 * 027 * * boolean render(DomainResource) : produce an HTML representation suitable for runtime / documentation, and insert it into the resource. Return true of any extensions encountered 028 * * boolean render(XhtmlNode, Resource: produce an HTML representation, and fill out the provided node with it. Return true of any extensions encountered 029 * * XhtmlNode build(DomainResource): same as render(DomainResource) but also return the XHtmlNode 030 * 031 * * String display(Base) : produce a plan text concise representation that serves to describe the resource 032 * * void display(XhtmlNode, Base) : produce a plan text concise representation that serves to describe the resource 033 * 034 * * 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) 035 * 036 * 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 037 * 038 * @author graha 039 * 040 */ 041public class Renderer { 042 043 protected static final boolean DEBUG = false; 044 045 public static class RenderingStatus { 046 private boolean extensions; 047 048 public void setExtensions(boolean b) { 049 extensions = b; 050 } 051 052 public boolean getExtensions() { 053 return extensions; 054 } 055 056 public boolean isShowCodeDetails() { 057 // TODO Auto-generated method stub 058 return false; 059 } 060 061 } 062 protected RenderingContext context; 063 064 public Renderer(RenderingContext context) { 065 this.context = context; 066 } 067 068 public Renderer(IWorkerContext worker) { 069 this.context = new RenderingContext(worker, new MarkDownProcessor(Dialect.COMMON_MARK), ValidationOptions.defaults(), "http://hl7.org/fhir/R5", "", null, ResourceRendererMode.END_USER, GenerationRules.IG_PUBLISHER); 070 } 071 072 073 protected String formatPhrase(String theMessage, Object... theMessageArguments) { 074 return context.formatPhrase(theMessage, theMessageArguments); 075 } 076 077 public void genStandardsStatus(XhtmlNode td, StandardsStatus ss) { 078 if (ss != null) { 079 td.tx(" "); 080 XhtmlNode a = td.ah(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "versions.html#std-process"), (context.formatPhrase(RenderingContext.REND_STANDARDS, ss.toDisplay()))); 081 a.style("padding-left: 3px; padding-right: 3px; border: 1px grey solid; font-weight: bold; color: black; background-color: "+ss.getColor()); 082 a.tx(ss.getAbbrev()); 083 } 084 } 085 086 protected XhtmlNode renderStatus(Base b, XhtmlNode x) { 087 if (b == null || context.getChangeVersion() == null) { 088 return x; 089 } 090 VersionComparisonAnnotation vca = (VersionComparisonAnnotation) b.getUserData(VersionComparisonAnnotation.USER_DATA_NAME); 091 if (vca == null) { 092 return x; 093 } 094 switch (vca.getType()) { 095 case Added: 096 XhtmlNode spanOuter = x.span("border: solid 1px #dddddd; margin: 2px; padding: 2px", null); 097 XhtmlNode spanInner = spanOuter.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", (context.formatPhrase(RenderingContext.REND_SINCE_ADDED, context.getChangeVersion()))); 098 spanInner.img("icon-change-add.png", "icon"); 099 spanInner.tx(" "+context.formatPhrase(RenderingContext.REND_ADDED)); 100 return spanOuter; 101 case Changed: 102 spanOuter = x.span("border: solid 1px #dddddd; margin: 2px; padding: 2px", null); 103 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())); 104 spanInner.img("icon-change-edit.png", "icon"); 105 spanInner.tx(" "+context.formatPhrase(RenderingContext.REND_CHANGED)); 106 return spanOuter; 107 case Deleted: 108 spanOuter = x.span("border: solid 1px #dddddd; margin: 2px; padding: 2px", null); 109 spanInner = spanOuter.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", (context.formatPhrase(RenderingContext.GENERAL_REMOVED_SINCE, context.getChangeVersion()))); 110 spanInner.img("icon-change-remove.png", "icon"); 111 spanInner.tx(" "+context.formatPhrase(RenderingContext.REND_REMOVED)); 112 return spanOuter.strikethrough(); 113 default: 114 return x; 115 } 116 } 117 118 protected XhtmlNode renderStatusDiv(Base b, XhtmlNode x) { 119 if (b == null || context.getChangeVersion() == null) { 120 return x; 121 } 122 VersionComparisonAnnotation vca = (VersionComparisonAnnotation) b.getUserData(VersionComparisonAnnotation.USER_DATA_NAME); 123 if (vca == null) { 124 return x; 125 } 126 switch (vca.getType()) { 127 case Added: 128 XhtmlNode divOuter = x.div("border: solid 1px #dddddd; margin: 2px; padding: 2px"); 129 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()))); 130 spanInner.img("icon-change-add.png", "icon"); 131 spanInner.tx(" "+context.formatPhrase(RenderingContext.REND_ADDED)); 132 return divOuter; 133 case Changed: 134 divOuter = x.div("border: solid 1px #dddddd; margin: 2px; padding: 2px"); 135 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())); 136 spanInner.img("icon-change-edit.png", "icon"); 137 spanInner.tx(" "+context.formatPhrase(RenderingContext.REND_CHANGED)); 138 return divOuter; 139 case Deleted: 140 divOuter = x.div("border: solid 1px #dddddd; margin: 2px; padding: 2px"); 141 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()))); 142 spanInner.img("icon-change-remove.png", "icon"); 143 spanInner.tx(" "+context.formatPhrase(RenderingContext.REND_REMOVED)); 144 return divOuter.strikethrough(); 145 default: 146 return x; 147 } 148 } 149 150 151 protected XhtmlNode renderStatusRow(Base b, XhtmlNode tbl, XhtmlNode tr) { 152 if (b == null || context.getChangeVersion() == null) { 153 return tr.td(); 154 } 155 VersionComparisonAnnotation vca = (VersionComparisonAnnotation) b.getUserData(VersionComparisonAnnotation.USER_DATA_NAME); 156 if (vca == null) { 157 return tr.td(); 158 } 159 switch (vca.getType()) { 160 case Added: 161 if (tbl.isClass("grid")) { 162 tr.style("border: solid 1px #dddddd; margin: 2px; padding: 2px"); 163 } 164 XhtmlNode td = tr.td(); 165 XhtmlNode span = td.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", (context.formatPhrase(RenderingContext.REND_ROW_SINCE, context.getChangeVersion()))); 166 span.img("icon-change-add.png", "icon"); 167 span.tx(" "+ context.formatPhrase(RenderingContext.REND_ADDED)); 168 XhtmlNode x = new XhtmlNode(NodeType.Element, "holder"); 169 x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", context.formatPhrase(RenderingContext.REND_ROW_SINCE, context.getChangeVersion())).tx(" "); 170 tr.styleCells(x); 171 return td; 172 case Changed: 173 td = tr.td(); 174 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())); 175 span.img("icon-change-edit.png", "icon"); 176 span.tx(" "+ context.formatPhrase(RenderingContext.REND_CHANGED)); 177 return td; 178 case Deleted: 179 tr.style("text-decoration: line-through"); 180 td = tr.td(); 181 span = td.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", context.formatPhrase(RenderingContext.GENERAL_REMOVED_SINCE, context.getChangeVersion())); 182 span.img("icon-change-remove.png", "icon"); 183 span.tx(" "+ context.formatPhrase(RenderingContext.REND_REMOVED)); 184 x = new XhtmlNode(NodeType.Element, "holder"); 185 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(" "); 186 tr.styleCells(x); 187 return td; 188 default: 189 return tr.td(); 190 } 191 } 192 193 public static void renderStatusSummary(RenderingContext context, Base base, XhtmlNode x, String version, String... metadataFields) { 194 if (base.hasUserData(VersionComparisonAnnotation.USER_DATA_NAME)) { 195 VersionComparisonAnnotation self = (VersionComparisonAnnotation) base.getUserData(VersionComparisonAnnotation.USER_DATA_NAME); 196 switch (self.getType()) { 197 case Added: 198 XhtmlNode spanInner = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", context.formatPhrase(RenderingContext.REND_SINCE_ADDED, version)); 199 spanInner.img("icon-change-add.png", "icon"); 200 spanInner.tx(" "+context.formatPhrase(RenderingContext.REND_ADDED)); 201 return; 202 case Changed: 203 if (self.getComp().noChangeOtherThanMetadata(metadataFields)) { 204 x.span("color: #eeeeee").tx("n/c"); 205 return; 206 } else { 207 spanInner = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", 208 self.getOriginal() != null ? context.formatPhrase(RenderingContext.REND_SINCE_CHANGED_WAS, version, self.getOriginal()) : context.formatPhrase(RenderingContext.REND_SINCE_CHANGED, version)); 209 spanInner.img("icon-change-edit.png", "icon"); 210 spanInner.tx(" "+context.formatPhrase(RenderingContext.REND_CHANGED)); 211 } 212 return; 213 case Deleted: 214 spanInner = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", context.formatPhrase(RenderingContext.GENERAL_REMOVED_SINCE, version)); 215 spanInner.img("icon-change-remove.png", "icon"); 216 spanInner.tx(" "+context.formatPhrase(RenderingContext.REND_REMOVED)); 217 return; 218 default: 219 x.span("color: #eeeeee").tx("n/c"); 220 return; 221 } 222 } else { 223 x.span("color: #eeeeee").tx("--"); 224 } 225 } 226 227 228 public String egt(@SuppressWarnings("rawtypes") Enumeration<? extends Enum> value) { 229 if (value == null || !value.hasPrimitiveValue()) { 230 return null; 231 } else { 232 return (value == null || !value.hasPrimitiveValue()) ? null : value.asStringValue(); 233 } 234 } 235 236 public String toStr(int value) { 237 return Integer.toString(value); 238 } 239 240 public String toStr(Date value) { 241 return value.toString(); 242 } 243 244 protected ResourceWrapper wrapNC(DataType type) { 245 return ResourceWrapper.forType(context.getContextUtilities(), type); 246 } 247 248 protected ResourceWrapper wrap(Resource resource) { 249 return ResourceWrapper.forResource(context.getContextUtilities(), resource); 250 } 251 protected ResourceWrapper wrapWC(ResourceWrapper resource, DataType type) { 252 return ResourceWrapper.forType(context.getContextUtilities(), resource, type); 253 } 254 255 protected String getTranslatedCode(ResourceWrapper child) { 256 return context.getTranslatedCode(child.primitiveValue(), child.getCodeSystemUri()); 257 } 258 259}