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