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