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