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