001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.html;
014
015import org.apache.juneau.internal.*;
016
017/**
018 * A basic template for the HTML doc serializer.
019 *
020 * <p>
021 * This class can be subclassed to customize page rendering.
022 */
023public class BasicHtmlDocTemplate implements HtmlDocTemplate {
024
025   @Override /* HtmlDocTemplate */
026   public void writeTo(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception {
027      w.sTag("html").nl(0);
028      w.sTag(1, "head").nl(1);
029      head(session, w, o);
030      w.eTag(1, "head").nl(1);
031      w.sTag(1, "body").nl(1);
032      body(session, w, o);
033      w.eTag(1, "body").nl(1);
034      w.eTag("html").nl(0);
035   }
036
037   /**
038    * Renders the contents of the <code><xt>&lt;head&gt;</xt></code> element.
039    *
040    * @param session The current serializer session.
041    * @param w The writer being written to.
042    * @param o The object being serialized.
043    * @throws Exception Any exception can be thrown.
044    */
045   protected void head(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception {
046
047      String[] head = session.getHead();
048      for (int i = 0; i < head.length; i++)
049         w.sIf(i > 0).appendln(2, session.resolve(head[i]));
050
051      if (hasStyle(session)) {
052         w.sTag(2, "style").nl(2);
053         style(session, w, o);
054         w.ie(2).eTag("style").nl(2);
055      }
056      if (hasScript(session)) {
057         w.sTag(2, "script").nl(2);
058         script(session, w, o);
059         w.ie(2).eTag("script").nl(2);
060      }
061   }
062
063   /**
064    * Renders the contents of the <code><xt>&lt;head&gt;</xt>/<xt>&lt;style&gt;</xt></code> element.
065    *
066    * @param session The current serializer session.
067    * @param w The writer being written to.
068    * @param o The object being serialized.
069    * @throws Exception Any exception can be thrown.
070    */
071   protected void style(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception {
072      int i = 0;
073      for (String s : session.getStylesheet())
074         w.sIf(i++ > 0).append(3, "@import ").q().append(session.resolveUri(session.resolve(s))).q().appendln(";");
075      if (session.isNowrap())
076         w.appendln(3, "div.data * {white-space:nowrap;} ");
077      for (String s : session.getStyle())
078         w.sIf(i++ > 0).appendln(3, session.resolve(s));
079      for (HtmlWidget hw : session.getWidgets())
080         w.sIf(i++ > 0).appendln(3, session.resolve(hw.getStyle(session.getVarResolver())));
081   }
082
083   /**
084    * Renders the contents of the <code><xt>&lt;head&gt;</xt>/<xt>&lt;script&gt;</xt></code> element.
085    *
086    * @param session The current serializer session.
087    * @param w The writer being written to.
088    * @param o The object being serialized.
089    * @throws Exception Any exception can be thrown.
090    */
091   protected void script(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception {
092      int i = 0;
093      for (String s : session.getScript())
094         w.sIf(i++ > 0).append(3, session.resolve(s)).append('\n'); // Must always append a newline even if whitespace disabled!
095      for (HtmlWidget hw : session.getWidgets())
096         w.sIf(i++ > 0).append(3, session.resolve(hw.getScript(session.getVarResolver()))).append('\n'); // Must always append a newline even if whitespace disabled!
097   }
098
099   /**
100    * Renders the contents of the <code><xt>&lt;body&gt;</xt></code> element.
101    *
102    * @param session The current serializer session.
103    * @param w The writer being written to.
104    * @param o The object being serialized.
105    * @throws Exception Any exception can be thrown.
106    */
107   protected void body(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception {
108
109      if (hasHeader(session)) {
110         w.sTag(2, "header").nl(2);
111         header(session, w, o);
112         w.ie(2).eTag("header").nl(2);
113      }
114
115      if (hasNav(session)) {
116         w.sTag(2, "nav").nl(2);
117         nav(session, w, o);
118         w.ie(2).eTag("nav").nl(2);
119      }
120
121      w.sTag(2, "section").nl(2);
122
123      w.sTag(3, "article").nl(3);
124      article(session, w, o);
125      w.ie(3).eTag("article").nl(3);
126
127      if (hasAside(session)) {
128         w.sTag(3, "aside").nl(3);
129         aside(session, w, o);
130         w.ie(3).eTag("aside").nl(3);
131      }
132
133      w.ie(2).eTag("section").nl(2);
134
135      if (hasFooter(session)) {
136         w.sTag(2, "footer").nl(2);
137         footer(session, w, o);
138         w.ie(2).eTag("footer").nl(2);
139      }
140   }
141
142   /**
143    * Renders the contents of the <code><xt>&lt;body&gt;</xt>/<xt>&lt;header&gt;</xt></code> element.
144    *
145    * @param session The current serializer session.
146    * @param w The writer being written to.
147    * @param o The object being serialized.
148    * @throws Exception Any exception can be thrown.
149    */
150   protected void header(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception {
151      // Write the title of the page.
152      String[] header = session.getHeader();
153      for (int i = 0; i < header.length; i++)
154         w.sIf(i > 0).appendln(3, session.resolve(header[i]));
155   }
156
157   /**
158    * Renders the contents of the <code><xt>&lt;body&gt;</xt>/<xt>&lt;nav&gt;</xt></code> element.
159    *
160    * @param session The current serializer session.
161    * @param w The writer being written to.
162    * @param o The object being serialized.
163    * @throws Exception Any exception can be thrown.
164    */
165   protected void nav(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception {
166      String[] links = session.getNavLinks();
167      if (links.length > 0 && ! ArrayUtils.contains("NONE", links)) {
168         w.sTag(3, "ol").nl(3);
169         for (String l : links) {
170            w.sTag(4, "li");
171            l = session.resolve(l);
172            if (l.matches("(?s)\\S+\\:.*")) {
173               int i = l.indexOf(':');
174               String key = l.substring(0, i);
175               String val = l.substring(i+1).trim();
176               if (val.startsWith("<"))
177                  w.nl(4).appendln(5, val);
178               else
179                  w.oTag("a").attr("href", session.resolveUri(val), true).cTag().text(key, true).eTag("a");
180               w.eTag("li").nl(4);
181            } else {
182               w.nl(4).appendln(5, l);
183               w.eTag(4, "li").nl(4);
184            }
185         }
186         w.eTag(3, "ol").nl(3);
187      }
188      String[] nav = session.getNav();
189      if (nav.length > 0) {
190         for (int i = 0; i < nav.length; i++)
191            w.sIf(i > 0).appendln(3, session.resolve(nav[i]));
192      }
193   }
194
195   /**
196    * Renders the contents of the <code><xt>&lt;body&gt;</xt>/<xt>&lt;aside&gt;</xt></code> element.
197    *
198    * @param session The current serializer session.
199    * @param w The writer being written to.
200    * @param o The object being serialized.
201    * @throws Exception Any exception can be thrown.
202    */
203   protected void aside(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception {
204      String[] aside = session.getAside();
205      for (int i = 0; i < aside.length; i++)
206         w.sIf(i > 0).appendln(4, session.resolve(aside[i]));
207   }
208
209   /**
210    * Renders the contents of the <code><xt>&lt;body&gt;</xt>/<xt>&lt;article&gt;</xt></code> element.
211    *
212    * @param session The current serializer session.
213    * @param w The writer being written to.
214    * @param o The object being serialized.
215    * @throws Exception Any exception can be thrown.
216    */
217   protected void article(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception {
218      // To allow for page formatting using CSS, we encapsulate the data inside two div tags:
219      // <div class='outerdata'><div class='data' id='data'>...</div></div>
220      w.oTag(4, "div").attr("class","outerdata").append('>').nl(4);
221      w.oTag(5, "div").attr("class","data").attr("id", "data").append('>').nl(5);
222
223      if (o == null) {
224         w.append(6, "<null/>").nl(6);
225      } else if (ObjectUtils.isEmpty(o)){
226         String m = session.getNoResultsMessage();
227         if (exists(m))
228            w.append(6, session.resolve(m)).nl(6);
229      } else {
230         session.indent = 6;
231         w.flush();
232         session.parentSerialize(w, o);
233      }
234
235      w.ie(5).eTag("div").nl(5);
236      w.ie(4).eTag("div").nl(4);
237   }
238
239   /**
240    * Renders the contents of the <code><xt>&lt;body&gt;</xt>/<xt>&lt;footer&gt;</xt></code> element.
241    *
242    * @param session The current serializer session.
243    * @param w The writer being written to.
244    * @param o The object being serialized.
245    * @throws Exception Any exception can be thrown.
246    */
247   protected void footer(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception {
248      String[] footer = session.getFooter();
249      for (int i = 0; i < footer.length; i++)
250         w.sIf(i > 0).appendln(3, session.resolve(footer[i]));
251   }
252
253   /**
254    * Returns <jk>true</jk> if this page should render a <code><xt>&lt;head&gt;</xt>/<xt>&lt;style&gt;</xt></code> element.
255    *
256    * @param session The current serializer session.
257    * @return A boolean flag.
258    */
259   protected boolean hasStyle(HtmlDocSerializerSession session) {
260      return true;
261   }
262
263   /**
264    * Returns <jk>true</jk> if this page should render a <code><xt>&lt;head&gt;</xt>/<xt>&lt;script&gt;</xt></code> element.
265    *
266    * @param session The current serializer session.
267    * @return A boolean flag.
268    */
269   protected boolean hasScript(HtmlDocSerializerSession session) {
270      return true;
271   }
272
273   /**
274    * Returns <jk>true</jk> if this page should render a <code><xt>&lt;body&gt;</xt>/<xt>&lt;header&gt;</xt></code>
275    * element.
276    *
277    * @param session The current serializer session.
278    * @return A boolean flag.
279    */
280   protected boolean hasHeader(HtmlDocSerializerSession session) {
281      return session.getHeader().length > 0;
282   }
283
284   /**
285    * Returns <jk>true</jk> if this page should render a <code><xt>&lt;body&gt;</xt>/<xt>&lt;nav&gt;</xt></code>
286    * element.
287    *
288    * @param session The current serializer session.
289    * @return A boolean flag.
290    */
291   protected boolean hasNav(HtmlDocSerializerSession session) {
292      return session.getNav().length > 0 || session.getNavLinks().length > 0;
293   }
294
295   /**
296    * Returns <jk>true</jk> if this page should render a <code><xt>&lt;body&gt;</xt>/<xt>&lt;aside&gt;</xt></code>
297    * element.
298    *
299    * @param session The current serializer session.
300    * @return A boolean flag.
301    */
302   protected boolean hasAside(HtmlDocSerializerSession session) {
303      return session.getAside().length > 0;
304   }
305
306   /**
307    * Returns <jk>true</jk> if this page should render a <code><xt>&lt;body&gt;</xt>/<xt>&lt;footer&gt;</xt></code>
308    * element.
309    *
310    * @param session The current serializer session.
311    * @return A boolean flag.
312    */
313   protected boolean hasFooter(HtmlDocSerializerSession session) {
314      return session.getFooter().length > 0;
315   }
316
317   private static boolean exists(String s) {
318      return s != null && ! "NONE".equals(s);
319   }
320}