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}