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