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, 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
073      int i = 0;
074      for (String s : session.getStylesheet())
075         w.sIf(i++ > 0).append(3, "@import ").q().append(session.resolveUri(s)).q().appendln(";");
076
077      if (session.isNowrap())
078         w.appendln(3, "div.data * {white-space:nowrap;} ");
079
080      for (String s : session.getStyle())
081         w.sIf(i > 0).appendln(3, s);
082   }
083
084   /**
085    * Renders the contents of the <code><xt>&lt;head&gt;</xt>/<xt>&lt;script&gt;</xt></code> element.
086    *
087    * @param session The current serializer session.
088    * @param w The writer being written to.
089    * @param o The object being serialized.
090    * @throws Exception Any exception can be thrown.
091    */
092   protected void script(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception {
093      int i = 0;
094      for (String s : session.getScript())
095         w.sIf(i++ > 0).append(3, s).append('\n'); // Must always append a newline even if whitespace disabled!
096   }
097
098   /**
099    * Renders the contents of the <code><xt>&lt;body&gt;</xt></code> element.
100    *
101    * @param session The current serializer session.
102    * @param w The writer being written to.
103    * @param o The object being serialized.
104    * @throws Exception Any exception can be thrown.
105    */
106   protected void body(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception {
107
108      if (hasHeader(session)) {
109         w.sTag(2, "header").nl(2);
110         header(session, w, o);
111         w.ie(2).eTag("header").nl(2);
112      }
113
114      if (hasNav(session)) {
115         w.sTag(2, "nav").nl(2);
116         nav(session, w, o);
117         w.ie(2).eTag("nav").nl(2);
118      }
119
120      w.sTag(2, "section").nl(2);
121
122      w.sTag(3, "article").nl(3);
123      article(session, w, o);
124      w.ie(3).eTag("article").nl(3);
125
126      if (hasAside(session)) {
127         w.sTag(3, "aside").nl(3);
128         aside(session, w, o);
129         w.ie(3).eTag("aside").nl(3);
130      }
131
132      w.ie(2).eTag("section").nl(2);
133
134      if (hasFooter(session)) {
135         w.sTag(2, "footer").nl(2);
136         footer(session, w, o);
137         w.ie(2).eTag("footer").nl(2);
138      }
139   }
140
141   /**
142    * Renders the contents of the <code><xt>&lt;body&gt;</xt>/<xt>&lt;header&gt;</xt></code> element.
143    *
144    * @param session The current serializer session.
145    * @param w The writer being written to.
146    * @param o The object being serialized.
147    * @throws Exception Any exception can be thrown.
148    */
149   protected void header(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception {
150      // Write the title of the page.
151      String[] header = session.getHeader();
152      for (int i = 0; i < header.length; i++)
153         w.sIf(i > 0).appendln(3, header[i]);
154   }
155
156   /**
157    * Renders the contents of the <code><xt>&lt;body&gt;</xt>/<xt>&lt;nav&gt;</xt></code> element.
158    *
159    * @param session The current serializer session.
160    * @param w The writer being written to.
161    * @param o The object being serialized.
162    * @throws Exception Any exception can be thrown.
163    */
164   protected void nav(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception {
165      String[] links = session.getNavLinks();
166      if (links.length > 0 && ! ArrayUtils.contains("NONE", links)) {
167         w.sTag(3, "ol").nl(3);
168         for (String l : links) {
169            w.sTag(4, "li");
170            if (l.matches("(?s)\\S+\\:.*")) {
171               int i = l.indexOf(':');
172               String key = l.substring(0, i);
173               String val = l.substring(i+1).trim();
174               if (val.startsWith("<"))
175                  w.nl(4).appendln(5, val);
176               else
177                  w.oTag("a").attr("href", session.resolveUri(val), true).cTag().text(key, true).eTag("a");
178               w.eTag("li").nl(4);
179            } else {
180               w.nl(4).appendln(5, l);
181               w.eTag(4, "li").nl(4);
182            }
183         }
184         w.eTag(3, "ol").nl(3);
185      }
186      String[] nav = session.getNav();
187      if (nav.length > 0) {
188         for (int i = 0; i < nav.length; i++)
189            w.sIf(i > 0).appendln(3, nav[i]);
190      }
191   }
192
193   /**
194    * Renders the contents of the <code><xt>&lt;body&gt;</xt>/<xt>&lt;aside&gt;</xt></code> element.
195    *
196    * @param session The current serializer session.
197    * @param w The writer being written to.
198    * @param o The object being serialized.
199    * @throws Exception Any exception can be thrown.
200    */
201   protected void aside(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception {
202      String[] aside = session.getAside();
203      for (int i = 0; i < aside.length; i++)
204         w.sIf(i > 0).appendln(4, aside[i]);
205   }
206
207   /**
208    * Renders the contents of the <code><xt>&lt;body&gt;</xt>/<xt>&lt;article&gt;</xt></code> element.
209    *
210    * @param session The current serializer session.
211    * @param w The writer being written to.
212    * @param o The object being serialized.
213    * @throws Exception Any exception can be thrown.
214    */
215   protected void article(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception {
216      // To allow for page formatting using CSS, we encapsulate the data inside two div tags:
217      // <div class='outerdata'><div class='data' id='data'>...</div></div>
218      w.oTag(4, "div").attr("class","outerdata").append('>').nl(4);
219      w.oTag(5, "div").attr("class","data").attr("id", "data").append('>').nl(5);
220
221      if (o == null) {
222         w.append(6, "<null/>").nl(6);
223      } else if (ObjectUtils.isEmpty(o)){
224         String m = session.getNoResultsMessage();
225         if (exists(m))
226            w.append(6, m).nl(6);
227      } else {
228         session.indent = 6;
229         w.flush();
230         session.parentSerialize(w, o);
231      }
232
233      w.ie(5).eTag("div").nl(5);
234      w.ie(4).eTag("div").nl(4);
235   }
236
237   /**
238    * Renders the contents of the <code><xt>&lt;body&gt;</xt>/<xt>&lt;footer&gt;</xt></code> element.
239    *
240    * @param session The current serializer session.
241    * @param w The writer being written to.
242    * @param o The object being serialized.
243    * @throws Exception Any exception can be thrown.
244    */
245   protected void footer(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception {
246      String[] footer = session.getFooter();
247      for (int i = 0; i < footer.length; i++)
248         w.sIf(i > 0).appendln(3, footer[i]);
249   }
250
251   /**
252    * Returns <jk>true</jk> if this page should render a <code><xt>&lt;head&gt;</xt>/<xt>&lt;style&gt;</xt></code> element.
253    *
254    * @param session The current serializer session.
255    * @return A boolean flag.
256    */
257   protected boolean hasStyle(HtmlDocSerializerSession session) {
258      return true;
259   }
260
261   /**
262    * Returns <jk>true</jk> if this page should render a <code><xt>&lt;head&gt;</xt>/<xt>&lt;script&gt;</xt></code> element.
263    *
264    * @param session The current serializer session.
265    * @return A boolean flag.
266    */
267   protected boolean hasScript(HtmlDocSerializerSession session) {
268      return true;
269   }
270
271   /**
272    * Returns <jk>true</jk> if this page should render a <code><xt>&lt;body&gt;</xt>/<xt>&lt;header&gt;</xt></code>
273    * element.
274    *
275    * @param session The current serializer session.
276    * @return A boolean flag.
277    */
278   protected boolean hasHeader(HtmlDocSerializerSession session) {
279      return session.getHeader().length > 0;
280   }
281
282   /**
283    * Returns <jk>true</jk> if this page should render a <code><xt>&lt;body&gt;</xt>/<xt>&lt;nav&gt;</xt></code>
284    * element.
285    *
286    * @param session The current serializer session.
287    * @return A boolean flag.
288    */
289   protected boolean hasNav(HtmlDocSerializerSession session) {
290      return session.getNav().length > 0 || session.getNavLinks().length > 0;
291   }
292
293   /**
294    * Returns <jk>true</jk> if this page should render a <code><xt>&lt;body&gt;</xt>/<xt>&lt;aside&gt;</xt></code>
295    * element.
296    *
297    * @param session The current serializer session.
298    * @return A boolean flag.
299    */
300   protected boolean hasAside(HtmlDocSerializerSession session) {
301      return session.getAside().length > 0;
302   }
303
304   /**
305    * Returns <jk>true</jk> if this page should render a <code><xt>&lt;body&gt;</xt>/<xt>&lt;footer&gt;</xt></code>
306    * element.
307    *
308    * @param session The current serializer session.
309    * @return A boolean flag.
310    */
311   protected boolean hasFooter(HtmlDocSerializerSession session) {
312      return session.getFooter().length > 0;
313   }
314
315   private static boolean exists(String s) {
316      return s != null && ! "NONE".equals(s);
317   }
318}