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