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}