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 java.util.*; 016 017import org.apache.juneau.*; 018import org.apache.juneau.annotation.*; 019import org.apache.juneau.collections.*; 020import org.apache.juneau.html.annotation.*; 021import org.apache.juneau.serializer.*; 022import org.apache.juneau.svl.*; 023 024/** 025 * Serializes POJOs to HTTP responses as HTML documents. 026 * 027 * <h5 class='topic'>Media types</h5> 028 * 029 * Handles <c>Accept</c> types: <bc>text/html</bc> 030 * <p> 031 * Produces <c>Content-Type</c> types: <bc>text/html</bc> 032 * 033 * <h5 class='topic'>Description</h5> 034 * 035 * Same as {@link HtmlSerializer}, except wraps the response in <code><xt><html></code>, 036 * <code><xt><head></code>, and <code><xt><body></code> tags so that it can be rendered in a browser. 037 * 038 * <p> 039 * Configurable properties are typically specified via <ja>@Rest(properties)</ja> and <ja>@RestMethod(properties)</ja> 040 * annotations, although they can also be set programmatically via the <c>RestResponse.setProperty()</c> method. 041 * 042 * <h5 class='section'>Example:</h5> 043 * <p class='bcode w800'> 044 * <ja>@Rest</ja>( 045 * messages=<js>"nls/AddressBookResource"</js>, 046 * properties={ 047 * <ja>@Property</ja>(name=HtmlDocSerializer.<jsf>HTMLDOC_title</jsf>, value=<js>"$L{title}"</js>), 048 * <ja>@Property</ja>(name=HtmlDocSerializer.<jsf>HTMLDOC_description</jsf>, value=<js>"$L{description}"</js>), 049 * <ja>@Property</ja>(name=HtmlDocSerializer.<jsf>HTMLDOC_navlinks</jsf>, value=<js>"{options:'servlet:/?method=OPTIONS',doc:'doc'}"</js>) 050 * } 051 * ) 052 * <jk>public class</jk> AddressBookResource <jk>extends</jk> BasicRestServletJena { 053 * </p> 054 * 055 * <p> 056 * Note that shortcut annotations are also provided for these particular settings: 057 * <p class='bcode w800'> 058 * <ja>@Rest</ja>( 059 * messages=<js>"nls/AddressBookResource"</js>, 060 * title=<js>"$L{title}"</js>, 061 * description=<js>"$L{description}"</js> 062 * ) 063 * <ja>@HtmlDocConfig</ja>( 064 * navlinks={ 065 * <js>"options: servlet:/?method=OPTIONS"</js>, 066 * <js>"doc: doc"</js> 067 * } 068 * ) 069 * </p> 070 * 071 * <p> 072 * The <c>$L{...}</c> variable represent localized strings pulled from the resource bundle identified by the 073 * <c>messages</c> annotation. 074 * <br>These variables are replaced at runtime based on the HTTP request locale. 075 * <br>Several built-in runtime variable types are defined, and the API can be extended to include user-defined variables. 076 */ 077@ConfigurableContext 078public class HtmlDocSerializer extends HtmlStrippedDocSerializer { 079 080 //------------------------------------------------------------------------------------------------------------------- 081 // Configurable properties 082 //------------------------------------------------------------------------------------------------------------------- 083 084 static final String PREFIX = "HtmlDocSerializer"; 085 086 /** 087 * Configuration property: Aside section contents. 088 * 089 * <h5 class='section'>Property:</h5> 090 * <ul class='spaced-list'> 091 * <li><b>ID:</b> {@link org.apache.juneau.html.HtmlDocSerializer#HTMLDOC_aside HTMLDOC_aside} 092 * <li><b>Name:</b> <js>"HtmlDocSerializer.aside.ls"</js> 093 * <li><b>Data type:</b> <c>List<String></c> 094 * <li><b>System property:</b> <c>HtmlDocSerializer.aside</c> 095 * <li><b>Environment variable:</b> <c>HTMLDOCSERIALIZER_ASIDE</c> 096 * <li><b>Default:</b> empty list 097 * <li><b>Session property:</b> <jk>true</jk> 098 * <li><b>Annotations:</b> 099 * <ul> 100 * <li class='ja'>{@link org.apache.juneau.html.annotation.HtmlDocConfig#aside()} 101 * </ul> 102 * <li><b>Methods:</b> 103 * <ul> 104 * <li class='jm'>{@link org.apache.juneau.html.HtmlDocSerializerBuilder#aside(String[])} 105 * </ul> 106 * </ul> 107 * 108 * <h5 class='section'>Description:</h5> 109 * <p> 110 * Allows you to specify the contents of the aside section on the HTML page. 111 * The aside section floats on the right of the page for providing content supporting the serialized content of 112 * the page. 113 * 114 * <p> 115 * By default, the aside section is empty. 116 * 117 * <h5 class='section'>Example:</h5> 118 * <p class='bcode w800'> 119 * <ja>@HtmlDocConfig</ja>( 120 * aside={ 121 * <js>"<ul>"</js>, 122 * <js>" <li>Item 1"</js>, 123 * <js>" <li>Item 2"</js>, 124 * <js>" <li>Item 3"</js>, 125 * <js>"</ul>"</js> 126 * } 127 * ) 128 * </p> 129 */ 130 public static final String HTMLDOC_aside = PREFIX + ".aside.ls"; 131 132 /** 133 * Configuration property: Float aside section contents. 134 * 135 * <h5 class='section'>Property:</h5> 136 * <ul class='spaced-list'> 137 * <li><b>ID:</b> {@link org.apache.juneau.html.HtmlDocSerializer#HTMLDOC_asideFloat HTMLDOC_asideFloat} 138 * <li><b>Name:</b> <js>"HtmlDocSerializer.asideFloat.s"</js> 139 * <li><b>Data type:</b> {@link org.apache.juneau.html.AsideFloat} 140 * <li><b>System property:</b> <c>HtmlDocSerializer.asideFloat</c> 141 * <li><b>Environment variable:</b> <c>HTMLDOCSERIALIZER_ASIDEFLOAT</c> 142 * <li><b>Default:</b> {@link org.apache.juneau.html.AsideFloat#DEFAULT} 143 * <li><b>Session property:</b> <jk>true</jk> 144 * <li><b>Annotations:</b> 145 * <ul> 146 * <li class='ja'>{@link org.apache.juneau.html.annotation.HtmlDocConfig#asideFloat()} 147 * </ul> 148 * <li><b>Methods:</b> 149 * <ul> 150 * <li class='jm'>{@link org.apache.juneau.html.HtmlDocSerializerBuilder#asideFloat(Float)} 151 * </ul> 152 * </ul> 153 * 154 * <h5 class='section'>Description:</h5> 155 * <p> 156 * Allows you to position the aside contents of the page around the main contents. 157 * 158 * <p> 159 * By default, the aside section is floated to the right. 160 * 161 * <h5 class='section'>Example:</h5> 162 * <p class='bcode w800'> 163 * <ja>@HtmlDocConfig</ja>( 164 * aside={ 165 * <js>"<ul>"</js>, 166 * <js>" <li>Item 1"</js>, 167 * <js>" <li>Item 2"</js>, 168 * <js>" <li>Item 3"</js>, 169 * <js>"</ul>"</js> 170 * }, 171 * asideFloat=<js>"RIGHT"</js> 172 * ) 173 * </p> 174 */ 175 public static final String HTMLDOC_asideFloat = PREFIX + ".asideFloat.s"; 176 177 /** 178 * Configuration property: Footer section contents. 179 * 180 * <h5 class='section'>Property:</h5> 181 * <ul class='spaced-list'> 182 * <li><b>ID:</b> {@link org.apache.juneau.html.HtmlDocSerializer#HTMLDOC_footer HTMLDOC_footer} 183 * <li><b>Name:</b> <js>"HtmlDocSerializer.footer.ls"</js> 184 * <li><b>Data type:</b> <c>List<String></c> 185 * <li><b>System property:</b> <c>HtmlDocSerializer.footer</c> 186 * <li><b>Environment variable:</b> <c>HTMLDOCSERIALIZER_FOOTER</c> 187 * <li><b>Default:</b> empty list 188 * <li><b>Session property:</b> <jk>true</jk> 189 * <li><b>Annotations:</b> 190 * <ul> 191 * <li class='ja'>{@link org.apache.juneau.html.annotation.HtmlDocConfig#footer()} 192 * </ul> 193 * <li><b>Methods:</b> 194 * <ul> 195 * <li class='jm'>{@link org.apache.juneau.html.HtmlDocSerializerBuilder#footer(String[])} 196 * </ul> 197 * </ul> 198 * 199 * <h5 class='section'>Description:</h5> 200 * <p> 201 * Allows you to specify the contents of the footer section on the HTML page. 202 * 203 * <p> 204 * By default, the footer section is empty. 205 * 206 * <h5 class='section'>Example:</h5> 207 * <p class='bcode w800'> 208 * <ja>@HtmlDocConfig</ja>( 209 * footer={ 210 * <js>"<b>This interface is great!</b>"</js> 211 * } 212 * ) 213 * </p> 214 */ 215 public static final String HTMLDOC_footer = PREFIX + ".footer.ls"; 216 217 /** 218 * Configuration property: Additional head section content. 219 * 220 * <h5 class='section'>Property:</h5> 221 * <ul class='spaced-list'> 222 * <li><b>ID:</b> {@link org.apache.juneau.html.HtmlDocSerializer#HTMLDOC_head HTMLDOC_head} 223 * <li><b>Name:</b> <js>"HtmlDocSerializer.head.ls"</js> 224 * <li><b>Data type:</b> <c>List<String></c> 225 * <li><b>System property:</b> <c>HtmlDocSerializer.head</c> 226 * <li><b>Environment variable:</b> <c>HTMLDOCSERIALIZER_HEAD</c> 227 * <li><b>Default:</b> empty list 228 * <li><b>Session property:</b> <jk>true</jk> 229 * <li><b>Annotations:</b> 230 * <ul> 231 * <li class='ja'>{@link org.apache.juneau.html.annotation.HtmlDocConfig#head()} 232 * </ul> 233 * <li><b>Methods:</b> 234 * <ul> 235 * <li class='jm'>{@link org.apache.juneau.html.HtmlDocSerializerBuilder#head(String[])} 236 * </ul> 237 * </ul> 238 * 239 * <h5 class='section'>Description:</h5> 240 * <p> 241 * Adds the specified HTML content to the head section of the page. 242 * 243 * <h5 class='section'>Example:</h5> 244 * <p class='bcode w800'> 245 * <ja>@Rest</ja>( 246 * properties={ 247 * <ja>@Property</ja>(name=HtmlDocSerializer.<jsf>HTMLDOC_links</jsf>, 248 * value=<js>"['<link rel=\"icon\" href=\"htdocs/mypageicon.ico\">']"</js>) 249 * } 250 * ) 251 * </p> 252 * 253 * <p> 254 * A shortcut on <ja>@Rest</ja> is also provided for this setting: 255 * <p class='bcode w800'> 256 * <ja>@HtmlDocConfig</ja>( 257 * head={ 258 * <js>"<link rel='icon' href='$U{servlet:/htdocs/mypageicon.ico}'>"</js> 259 * } 260 * ) 261 * </p> 262 */ 263 public static final String HTMLDOC_head = PREFIX + ".head.ls"; 264 265 /** 266 * Configuration property: Header section contents. 267 * 268 * <h5 class='section'>Property:</h5> 269 * <ul class='spaced-list'> 270 * <li><b>ID:</b> {@link org.apache.juneau.html.HtmlDocSerializer#HTMLDOC_header HTMLDOC_header} 271 * <li><b>Name:</b> <js>"HtmlDocSerializer.header.ls"</js> 272 * <li><b>Data type:</b> <c>List<String></c> 273 * <li><b>System property:</b> <c>HtmlDocSerializer.header</c> 274 * <li><b>Environment variable:</b> <c>HTMLDOCSERIALIZER_HEADER</c> 275 * <li><b>Default:</b> empty list 276 * <li><b>Session property:</b> <jk>true</jk> 277 * <li><b>Annotations:</b> 278 * <ul> 279 * <li class='ja'>{@link org.apache.juneau.html.annotation.HtmlDocConfig#header()} 280 * </ul> 281 * <li><b>Methods:</b> 282 * <ul> 283 * <li class='jm'>{@link org.apache.juneau.html.HtmlDocSerializerBuilder#header(String[])} 284 * </ul> 285 * </ul> 286 * 287 * <h5 class='section'>Description:</h5> 288 * <p> 289 * Allows you to override the contents of the header section on the HTML page. 290 * The header section normally contains the title and description at the top of the page. 291 * 292 * <h5 class='section'>Example:</h5> 293 * <p class='bcode w800'> 294 * <ja>@HtmlDocConfig</ja>( 295 * header={ 296 * <js>"<h1>My own header</h1>"</js> 297 * } 298 * ) 299 * </p> 300 */ 301 public static final String HTMLDOC_header = PREFIX + ".header.ls"; 302 303 /** 304 * Configuration property: Nav section contents. 305 * 306 * <h5 class='section'>Property:</h5> 307 * <ul class='spaced-list'> 308 * <li><b>ID:</b> {@link org.apache.juneau.html.HtmlDocSerializer#HTMLDOC_nav HTMLDOC_nav} 309 * <li><b>Name:</b> <js>"HtmlDocSerializer.nav.ls"</js> 310 * <li><b>Data type:</b> <c>List<String></c> 311 * <li><b>System property:</b> <c>HtmlDocSerializer.nav</c> 312 * <li><b>Environment variable:</b> <c>HTMLDOCSERIALIZER_NAV</c> 313 * <li><b>Default:</b> empty list 314 * <li><b>Session property:</b> <jk>true</jk> 315 * <li><b>Annotations:</b> 316 * <ul> 317 * <li class='ja'>{@link org.apache.juneau.html.annotation.HtmlDocConfig#nav()} 318 * </ul> 319 * <li><b>Methods:</b> 320 * <ul> 321 * <li class='jm'>{@link org.apache.juneau.html.HtmlDocSerializerBuilder#nav(String[])} 322 * </ul> 323 * </ul> 324 * 325 * <h5 class='section'>Description:</h5> 326 * <p> 327 * Allows you to override the contents of the nav section on the HTML page. 328 * The nav section normally contains the page links at the top of the page. 329 * 330 * <h5 class='section'>Example:</h5> 331 * <p class='bcode w800'> 332 * <ja>@HtmlDocConfig</ja>( 333 * nav={ 334 * <js>"<p class='special-navigation'>This is my special navigation content</p>"</js> 335 * } 336 * ) 337 * </p> 338 * 339 * <p> 340 * When this property is specified, the {@link #HTMLDOC_navlinks} property is ignored. 341 */ 342 public static final String HTMLDOC_nav = PREFIX + ".nav.ls"; 343 344 /** 345 * Configuration property: Page navigation links. 346 * 347 * <h5 class='section'>Property:</h5> 348 * <ul class='spaced-list'> 349 * <li><b>ID:</b> {@link org.apache.juneau.html.HtmlDocSerializer#HTMLDOC_navlinks HTMLDOC_navlinks} 350 * <li><b>Name:</b> <js>"HtmlDocSerializer.navlinks.ls"</js> 351 * <li><b>Data type:</b> <c>List<String></c> 352 * <li><b>System property:</b> <c>HtmlDocSerializer.navlinks</c> 353 * <li><b>Environment variable:</b> <c>HTMLDOCSERIALIZER_NAVLINKS</c> 354 * <li><b>Default:</b> empty list 355 * <li><b>Session property:</b> <jk>true</jk> 356 * <li><b>Annotations:</b> 357 * <ul> 358 * <li class='ja'>{@link org.apache.juneau.html.annotation.HtmlDocConfig#navlinks()} 359 * </ul> 360 * <li><b>Methods:</b> 361 * <ul> 362 * <li class='jm'>{@link org.apache.juneau.html.HtmlDocSerializerBuilder#navlinks(String[])} 363 * </ul> 364 * </ul> 365 * 366 * <h5 class='section'>Description:</h5> 367 * <p> 368 * Adds a list of hyperlinks immediately under the title and description but above the content of the page. 369 * 370 * <p> 371 * This can be used to provide convenient hyperlinks when viewing the REST interface from a browser. 372 * 373 * <p> 374 * The value is an array of strings with two possible values: 375 * <ul> 376 * <li>A key-value pair representing a hyperlink label and href: 377 * <br><js>"google: http://google.com"</js> 378 * <li>Arbitrary HTML. 379 * </ul> 380 * 381 * <p> 382 * Relative URLs are considered relative to the servlet path. 383 * For example, if the servlet path is <js>"http://localhost/myContext/myServlet"</js>, and the 384 * URL is <js>"foo"</js>, the link becomes <js>"http://localhost/myContext/myServlet/foo"</js>. 385 * Absolute (<js>"/myOtherContext/foo"</js>) and fully-qualified (<js>"http://localhost2/foo"</js>) URLs 386 * can also be used in addition to various other protocols specified by {@link UriResolver} such as 387 * <js>"servlet:/..."</js>. 388 * 389 * <h5 class='section'>Example:</h5> 390 * <p> 391 * The <c>AddressBookResource</c> sample class uses this property... 392 * <p class='bcode w800'> 393 * <ja>@Rest</ja>( 394 * properties={ 395 * <ja>@Property</ja>(name=HtmlDocSerializer.<jsf>HTMLDOC_navlinks</jsf>, 396 * value=<js>"['options: servlet:/?method=OPTIONS', 'doc: doc']"</js>) 397 * } 398 * ) 399 * <jk>public class</jk> AddressBookResource <jk>extends</jk> BasicRestServletJena { 400 * </p> 401 * 402 * <p> 403 * A shortcut on <ja>@Rest</ja> is also provided for this setting: 404 * <p class='bcode w800'> 405 * <ja>@HtmlDocConfig</ja>( 406 * navlinks={ 407 * <js>"options: servlet:/?method=OPTIONS"</js>, 408 * <js>"doc: doc"</js> 409 * } 410 * ) 411 * <jk>public class</jk> AddressBookResource <jk>extends</jk> BasicRestServletJena { 412 * </p> 413 */ 414 public static final String HTMLDOC_navlinks = PREFIX + ".navlinks.ls"; 415 416 /** 417 * Configuration property: Add to the {@link #HTMLDOC_navlinks} property. 418 */ 419 public static final String HTMLDOC_navlinks_add = PREFIX + ".navlinks.ls/add"; 420 421 /** 422 * Configuration property: No-results message. 423 * 424 * <h5 class='section'>Property:</h5> 425 * <ul class='spaced-list'> 426 * <li><b>ID:</b> {@link org.apache.juneau.html.HtmlDocSerializer#HTMLDOC_noResultsMessage HTMLDOC_noResultsMessage} 427 * <li><b>Name:</b> <js>"HtmlDocSerializer.noResultsMessage.s"</js> 428 * <li><b>Data type:</b> <c>String</c> 429 * <li><b>System property:</b> <c>HtmlDocSerializer.noResultsMessage</c> 430 * <li><b>Environment variable:</b> <c>HTMLDOCSERIALIZER_NORESULTSMESSAGE</c> 431 * <li><b>Default:</b> <js>"<p>no results</p>"</js> 432 * <li><b>Session property:</b> <jk>false</jk> 433 * <li><b>Annotations:</b> 434 * <ul> 435 * <li class='ja'>{@link org.apache.juneau.html.annotation.HtmlDocConfig#noResultsMessage()} 436 * </ul> 437 * <li><b>Methods:</b> 438 * <ul> 439 * <li class='jm'>{@link org.apache.juneau.html.HtmlDocSerializerBuilder#noResultsMessage(String)} 440 * </ul> 441 * </ul> 442 * 443 * <h5 class='section'>Description:</h5> 444 * <p> 445 * Allows you to specify the string message used when trying to serialize an empty array or empty list. 446 * 447 * <h5 class='section'>Example:</h5> 448 * <p class='bcode w800'> 449 * <ja>@HtmlDocConfig</ja>( 450 * noResultsMessage=<js>"<b>This interface is great!</b>"</js> 451 * ) 452 * </p> 453 * 454 * <p> 455 * A value of <js>"NONE"</js> can be used to represent no value to differentiate it from an empty string. 456 */ 457 public static final String HTMLDOC_noResultsMessage = PREFIX + ".noResultsMessage.s"; 458 459 /** 460 * Configuration property: Prevent word wrap on page. 461 * 462 * <h5 class='section'>Property:</h5> 463 * <ul class='spaced-list'> 464 * <li><b>ID:</b> {@link org.apache.juneau.html.HtmlDocSerializer#HTMLDOC_nowrap HTMLDOC_nowrap} 465 * <li><b>Name:</b> <js>"HtmlDocSerializer.nowrap.b"</js> 466 * <li><b>Data type:</b> <jk>boolean</jk> 467 * <li><b>System property:</b> <c>HtmlDocSerializer.nowrap</c> 468 * <li><b>Environment variable:</b> <c>HTMLDOCSERIALIZER_NOWRAP</c> 469 * <li><b>Default:</b> <jk>false</jk> 470 * <li><b>Session property:</b> <jk>false</jk> 471 * <li><b>Annotations:</b> 472 * <ul> 473 * <li class='ja'>{@link org.apache.juneau.html.annotation.HtmlDocConfig#nowrap()} 474 * </ul> 475 * <li><b>Methods:</b> 476 * <ul> 477 * <li class='jm'>{@link org.apache.juneau.html.HtmlDocSerializerBuilder#nowrap(boolean)} 478 * <li class='jm'>{@link org.apache.juneau.html.HtmlDocSerializerBuilder#nowrap()} 479 * </ul> 480 * </ul> 481 * 482 * <h5 class='section'>Description:</h5> 483 * <p> 484 * Adds <js>"* {white-space:nowrap}"</js> to the CSS instructions on the page to prevent word wrapping. 485 */ 486 public static final String HTMLDOC_nowrap = PREFIX + ".nowrap.b"; 487 488 /** 489 * Configuration property: Javascript code. 490 * 491 * <h5 class='section'>Property:</h5> 492 * <ul class='spaced-list'> 493 * <li><b>ID:</b> {@link org.apache.juneau.html.HtmlDocSerializer#HTMLDOC_script HTMLDOC_script} 494 * <li><b>Name:</b> <js>"HtmlDocSerializer.script.ls"</js> 495 * <li><b>Data type:</b> <c>List<String></c> 496 * <li><b>System property:</b> <c>HtmlDocSerializer.script</c> 497 * <li><b>Environment variable:</b> <c>HTMLDOCSERIALIZER_SCRIPT</c> 498 * <li><b>Default:</b> empty list 499 * <li><b>Session property:</b> <jk>true</jk> 500 * <li><b>Annotations:</b> 501 * <ul> 502 * <li class='ja'>{@link org.apache.juneau.html.annotation.HtmlDocConfig#script()} 503 * </ul> 504 * <li><b>Methods:</b> 505 * <ul> 506 * <li class='jm'>{@link org.apache.juneau.html.HtmlDocSerializerBuilder#script(String[])} 507 * </ul> 508 * </ul> 509 * 510 * <h5 class='section'>Description:</h5> 511 * <p> 512 * Adds the specified Javascript code to the HTML page. 513 * 514 * <h5 class='section'>Example:</h5> 515 * <p class='bcode w800'> 516 * <ja>@Rest</ja>( 517 * properties={ 518 * <ja>@Property</ja>(name=HtmlDocSerializer.<jsf>HTMLDOC_script</jsf>, 519 * value=<js>"alert('hello!');"</js>) 520 * } 521 * ) 522 * </p> 523 * 524 * <p> 525 * A shortcut on <ja>@Rest</ja> is also provided for this setting: 526 * <p class='bcode w800'> 527 * <ja>@HtmlDocConfig</ja>( 528 * script={ 529 * <js>"alert('hello!');"</js> 530 * } 531 * ) 532 * </p> 533 */ 534 public static final String HTMLDOC_script = PREFIX + ".script.ls"; 535 536 /** 537 * Configuration property: Add to the {@link #HTMLDOC_script} property. 538 */ 539 public static final String HTMLDOC_script_add = PREFIX + ".script.ls/add"; 540 541 /** 542 * Configuration property: CSS style code. 543 * 544 * <h5 class='section'>Property:</h5> 545 * <ul class='spaced-list'> 546 * <li><b>ID:</b> {@link org.apache.juneau.html.HtmlDocSerializer#HTMLDOC_style HTMLDOC_style} 547 * <li><b>Name:</b> <js>"HtmlDocSerializer.style.ls"</js> 548 * <li><b>Data type:</b> <c>List<String></c> 549 * <li><b>System property:</b> <c>HtmlDocSerializer.style</c> 550 * <li><b>Environment variable:</b> <c>HTMLDOCSERIALIZER_STYLE</c> 551 * <li><b>Default:</b> empty list 552 * <li><b>Session property:</b> <jk>true</jk> 553 * <li><b>Annotations:</b> 554 * <ul> 555 * <li class='ja'>{@link org.apache.juneau.html.annotation.HtmlDocConfig#style()} 556 * </ul> 557 * <li><b>Methods:</b> 558 * <ul> 559 * <li class='jm'>{@link org.apache.juneau.html.HtmlDocSerializerBuilder#style(String[])} 560 * </ul> 561 * </ul> 562 * 563 * <h5 class='section'>Description:</h5> 564 * <p> 565 * Adds the specified CSS instructions to the HTML page. 566 * 567 * <h5 class='section'>Example:</h5> 568 * <p class='bcode w800'> 569 * <ja>@Rest</ja>( 570 * properties={ 571 * <ja>@Property</ja>(name=HtmlDocSerializer.<jsf>HTMLDOC_style</jsf>, 572 * value=<js>"h3 { color: red; }\nh5 { font-weight: bold; }"</js>) 573 * } 574 * ) 575 * </p> 576 * 577 * <p> 578 * A shortcut on <ja>@Rest</ja> is also provided for this setting: 579 * <p class='bcode w800'> 580 * <ja>@HtmlDocConfig</ja>( 581 * style={ 582 * <js>"h3 { color: red; }"</js>, 583 * <js>"h5 { font-weight: bold; }"</js> 584 * } 585 * ) 586 * </p> 587 */ 588 public static final String HTMLDOC_style = PREFIX + ".style.ls"; 589 590 /** 591 * Configuration property: Add to the {@link #HTMLDOC_style} property. 592 */ 593 public static final String HTMLDOC_style_add = PREFIX + ".style.ls/add"; 594 595 /** 596 * Configuration property: Stylesheet import URLs. 597 * 598 * <h5 class='section'>Property:</h5> 599 * <ul class='spaced-list'> 600 * <li><b>ID:</b> {@link org.apache.juneau.html.HtmlDocSerializer#HTMLDOC_stylesheet HTMLDOC_stylesheet} 601 * <li><b>Name:</b> <js>"HtmlDocSerializer.stylesheet.ls"</js> 602 * <li><b>Data type:</b> <c>List<String></c> 603 * <li><b>System property:</b> <c>HtmlDocSerializer.stylesheet</c> 604 * <li><b>Environment variable:</b> <c>HTMLDOCSERIALIZER_STYLESHEET</c> 605 * <li><b>Default:</b> empty list 606 * <li><b>Session property:</b> <jk>true</jk> 607 * <li><b>Annotations:</b> 608 * <ul> 609 * <li class='ja'>{@link org.apache.juneau.html.annotation.HtmlDocConfig#stylesheet()} 610 * </ul> 611 * <li><b>Methods:</b> 612 * <ul> 613 * <li class='jm'>{@link org.apache.juneau.html.HtmlDocSerializerBuilder#stylesheet(String[])} 614 * </ul> 615 * </ul> 616 * 617 * <h5 class='section'>Description:</h5> 618 * <p> 619 * Adds a link to the specified stylesheet URL. 620 * 621 * <p> 622 * Note that this stylesheet is controlled by the <code><ja>@Rest</ja>.stylesheet()</code> annotation. 623 */ 624 public static final String HTMLDOC_stylesheet = PREFIX + ".stylesheet.ls"; 625 626 /** 627 * Configuration property: Add to the {@link #HTMLDOC_stylesheet} property. 628 */ 629 public static final String HTMLDOC_stylesheet_add = PREFIX + ".stylesheet.ls/add"; 630 631 /** 632 * Configuration property: HTML document template. 633 * 634 * <h5 class='section'>Property:</h5> 635 * <ul class='spaced-list'> 636 * <li><b>ID:</b> {@link org.apache.juneau.html.HtmlDocSerializer#HTMLDOC_template HTMLDOC_template} 637 * <li><b>Name:</b> <js>"HtmlDocSerializer.template.c"</js> 638 * <li><b>Data type:</b> <code>Class<{@link org.apache.juneau.html.HtmlDocTemplate}></code> 639 * <li><b>Default:</b> {@link org.apache.juneau.html.BasicHtmlDocTemplate} 640 * <li><b>Session property:</b> <jk>false</jk> 641 * <li><b>Annotations:</b> 642 * <ul> 643 * <li class='ja'>{@link org.apache.juneau.html.annotation.HtmlDocConfig#template()} 644 * </ul> 645 * <li><b>Methods:</b> 646 * <ul> 647 * <li class='jm'>{@link org.apache.juneau.html.HtmlDocSerializerBuilder#template(Class)} 648 * </ul> 649 * </ul> 650 * 651 * <h5 class='section'>Description:</h5> 652 * <p> 653 * Specifies the template to use for serializing the page. 654 * 655 * <p> 656 * By default, the {@link BasicHtmlDocTemplate} class is used to construct the contents of the HTML page, but 657 * can be overridden with your own custom implementation class. 658 * 659 * <h5 class='section'>Example:</h5> 660 * <p class='bcode w800'> 661 * <ja>@HtmlDocConfig</ja>( 662 * template=MySpecialDocTemplate.<jk>class</jk> 663 * ) 664 * </p> 665 */ 666 public static final String HTMLDOC_template = PREFIX + ".template.c"; 667 668 /** 669 * Configuration property: HTML Widgets. 670 * 671 * <h5 class='section'>Property:</h5> 672 * <ul class='spaced-list'> 673 * <li><b>ID:</b> {@link org.apache.juneau.html.HtmlDocSerializer#HTMLDOC_widgets HTMLDOC_widgets} 674 * <li><b>Name:</b> <js>"HtmlDocSerializer.widgets.lo"</js> 675 * <li><b>Data type:</b><c>List<{@link org.apache.juneau.html.HtmlWidget}|Class<{@link org.apache.juneau.html.HtmlWidget}>></c> 676 * <li><b>Default:</b> empty list 677 * <li><b>Session property:</b> <jk>false</jk> 678 * <li><b>Annotations:</b> 679 * <ul> 680 * <li class='ja'>{@link HtmlDocConfig#widgets()} 681 * <li class='ja'>{@link org.apache.juneau.html.annotation.HtmlDocConfig#widgets()} 682 * </ul> 683 * <li><b>Methods:</b> 684 * <ul> 685 * <li class='jm'>{@link HtmlDocSerializerBuilder#widgets(Class...)} 686 * <li class='jm'>{@link HtmlDocSerializerBuilder#widgets(HtmlWidget...)} 687 * <li class='jm'>{@link HtmlDocSerializerBuilder#widgetsReplace(Class...)} 688 * <li class='jm'>{@link HtmlDocSerializerBuilder#widgetsReplace(HtmlWidget...)} 689 * </ul> 690 * </ul> 691 * 692 * <h5 class='section'>Description:</h5> 693 * <p> 694 * Defines widgets that can be used in conjunction with string variables of the form <js>"$W{name}"</js>to quickly 695 * generate arbitrary replacement text. 696 * 697 * Widgets resolve the following variables: 698 * <ul class='spaced-list'> 699 * <li><js>"$W{name}"</js> - Contents returned by {@link HtmlWidget#getHtml(VarResolverSession)}. 700 * <li><js>"$W{name.script}"</js> - Contents returned by {@link HtmlWidget#getScript(VarResolverSession)}. 701 * <br>The script contents are automatically inserted into the <xt><head/script></xt> section 702 * in the HTML page. 703 * <li><js>"$W{name.style}"</js> - Contents returned by {@link HtmlWidget#getStyle(VarResolverSession)}. 704 * <br>The styles contents are automatically inserted into the <xt><head/style></xt> section 705 * in the HTML page. 706 * </ul> 707 * 708 * <p> 709 * The following examples shows how to associate a widget with a REST method and then have it rendered in the links 710 * and aside section of the page: 711 * 712 * <p class='bcode w800'> 713 * <ja>@HtmlDocConfig</ja>( 714 * widgets={ 715 * MyWidget.<jk>class</jk> 716 * }, 717 * navlinks={ 718 * <js>"$W{MyWidget}"</js> 719 * }, 720 * aside={ 721 * <js>"Check out this widget: $W{MyWidget}"</js> 722 * } 723 * ) 724 * </p> 725 * 726 * <ul class='notes'> 727 * <li> 728 * Widgets are inherited from super classes, but can be overridden by reusing the widget name. 729 * </ul> 730 * 731 * <ul class='seealso'> 732 * <li class='link'>{@doc RestHtmlWidgets} 733 * </ul> 734 */ 735 public static final String HTMLDOC_widgets = PREFIX + ".widgets.lo"; 736 737 //------------------------------------------------------------------------------------------------------------------- 738 // Predefined instances 739 //------------------------------------------------------------------------------------------------------------------- 740 741 /** Default serializer, all default settings. */ 742 public static final HtmlDocSerializer DEFAULT = new HtmlDocSerializer(PropertyStore.DEFAULT); 743 744 745 //------------------------------------------------------------------------------------------------------------------- 746 // Instance 747 //------------------------------------------------------------------------------------------------------------------- 748 749 private final String[] style, stylesheet, script, navlinks, head, header, nav, aside, footer; 750 private final AsideFloat asideFloat; 751 private final String noResultsMessage; 752 private final boolean nowrap; 753 private final HtmlDocTemplate template; 754 private final Map<String,HtmlWidget> widgets; 755 756 private volatile HtmlSchemaDocSerializer schemaSerializer; 757 758 /** 759 * Constructor. 760 * 761 * @param ps The property store containing all the settings for this object. 762 */ 763 public HtmlDocSerializer(PropertyStore ps) { 764 this(ps, "text/html", (String)null); 765 } 766 767 /** 768 * Constructor. 769 * 770 * @param ps 771 * The property store containing all the settings for this object. 772 * @param produces 773 * The media type that this serializer produces. 774 * @param accept 775 * The accept media types that the serializer can handle. 776 * <p> 777 * Can contain meta-characters per the <c>media-type</c> specification of 778 * {@doc ExtRFC2616.section14.1} 779 * <p> 780 * If empty, then assumes the only media type supported is <c>produces</c>. 781 * <p> 782 * For example, if this serializer produces <js>"application/json"</js> but should handle media types of 783 * <js>"application/json"</js> and <js>"text/json"</js>, then the arguments should be: 784 * <p class='bcode w800'> 785 * <jk>super</jk>(ps, <js>"application/json"</js>, <js>"application/json",text/json"</js>); 786 * </p> 787 * <br>...or... 788 * <p class='bcode w800'> 789 * <jk>super</jk>(ps, <js>"application/json"</js>, <js>"*​/json"</js>); 790 * </p> 791 * <p> 792 * The accept value can also contain q-values. 793 */ 794 public HtmlDocSerializer(PropertyStore ps, String produces, String accept) { 795 super(ps, produces, accept); 796 style = getArrayProperty(HTMLDOC_style, String.class); 797 stylesheet = getArrayProperty(HTMLDOC_stylesheet, String.class); 798 script = getArrayProperty(HTMLDOC_script, String.class); 799 head = getArrayProperty(HTMLDOC_head, String.class); 800 header = getArrayProperty(HTMLDOC_header, String.class); 801 nav = getArrayProperty(HTMLDOC_nav, String.class); 802 aside = getArrayProperty(HTMLDOC_aside, String.class); 803 asideFloat = getProperty(HTMLDOC_asideFloat, AsideFloat.class, AsideFloat.RIGHT); 804 footer = getArrayProperty(HTMLDOC_footer, String.class); 805 nowrap = getBooleanProperty(HTMLDOC_nowrap, false); 806 navlinks = getArrayProperty(HTMLDOC_navlinks, String.class); 807 noResultsMessage = getStringProperty(HTMLDOC_noResultsMessage, "<p>no results</p>"); 808 template = getInstanceProperty(HTMLDOC_template, HtmlDocTemplate.class, BasicHtmlDocTemplate.class); 809 810 Map<String,HtmlWidget> widgets = new HashMap<>(); 811 for (HtmlWidget w : getInstanceArrayProperty(HTMLDOC_widgets, HtmlWidget.class, new HtmlWidget[0])) 812 widgets.put(w.getName(), w); 813 this.widgets = Collections.unmodifiableMap(widgets); 814 } 815 816 @Override /* Context */ 817 public HtmlDocSerializerBuilder builder() { 818 return new HtmlDocSerializerBuilder(getPropertyStore()); 819 } 820 821 /** 822 * Instantiates a new clean-slate {@link HtmlDocSerializerBuilder} object. 823 * 824 * <p> 825 * This is equivalent to simply calling <code><jk>new</jk> HtmlDocSerializerBuilder()</code>. 826 * 827 * <p> 828 * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies 829 * the settings of the object called on. 830 * 831 * @return A new {@link HtmlDocSerializerBuilder} object. 832 */ 833 public static HtmlDocSerializerBuilder create() { 834 return new HtmlDocSerializerBuilder(); 835 } 836 837 @Override /* Serializer */ 838 public HtmlDocSerializerSession createSession() { 839 return createSession(createDefaultSessionArgs()); 840 } 841 842 @Override /* Serializer */ 843 public HtmlDocSerializerSession createSession(SerializerSessionArgs args) { 844 return new HtmlDocSerializerSession(this, args); 845 } 846 847 @Override /* XmlSerializer */ 848 public HtmlSerializer getSchemaSerializer() { 849 if (schemaSerializer == null) 850 schemaSerializer = builder().build(HtmlSchemaDocSerializer.class); 851 return schemaSerializer; 852 } 853 854 //----------------------------------------------------------------------------------------------------------------- 855 // Properties 856 //----------------------------------------------------------------------------------------------------------------- 857 858 /** 859 * Aside section contents. 860 * 861 * @see #HTMLDOC_aside 862 * @return 863 * The overridden contents of the aside section on the HTML page. 864 */ 865 protected final String[] getAside() { 866 return aside; 867 } 868 869 /** 870 * Float side section contents. 871 * 872 * @see #HTMLDOC_asideFloat 873 * @return 874 * How to float the aside contents on the page. 875 */ 876 protected final AsideFloat getAsideFloat() { 877 return asideFloat; 878 } 879 880 /** 881 * Footer section contents. 882 * 883 * @see #HTMLDOC_footer 884 * @return 885 * The overridden contents of the footer section on the HTML page. 886 */ 887 protected final String[] getFooter() { 888 return footer; 889 } 890 891 /** 892 * Additional head section content. 893 * 894 * @see #HTMLDOC_head 895 * @return 896 * HTML content to add to the head section of the HTML page. 897 */ 898 protected final String[] getHead() { 899 return head; 900 } 901 902 /** 903 * Header section contents. 904 * 905 * @see #HTMLDOC_header 906 * @return 907 * The overridden contents of the header section on the HTML page. 908 */ 909 protected final String[] getHeader() { 910 return header; 911 } 912 913 /** 914 * Nav section contents. 915 * 916 * @see #HTMLDOC_nav 917 * @return 918 * The overridden contents of the nav section on the HTML page. 919 */ 920 protected final String[] getNav() { 921 return nav; 922 } 923 924 /** 925 * Page navigation links. 926 * 927 * @see #HTMLDOC_navlinks 928 * @return 929 * Navigation links to add to the HTML page. 930 */ 931 protected final String[] getNavlinks() { 932 return navlinks; 933 } 934 935 /** 936 * No-results message. 937 * 938 * @see #HTMLDOC_noResultsMessage 939 * @return 940 * The message used when serializing an empty array or empty list. 941 */ 942 protected final String getNoResultsMessage() { 943 return noResultsMessage; 944 } 945 946 /** 947 * Prevent word wrap on page. 948 * 949 * @see #HTMLDOC_nowrap 950 * @return 951 * <jk>true</jk> if <js>"* {white-space:nowrap}"</js> shoudl be added to the CSS instructions on the page to prevent word wrapping. 952 */ 953 protected final boolean isNowrap() { 954 return nowrap; 955 } 956 957 /** 958 * Javascript code. 959 * 960 * @see #HTMLDOC_script 961 * @return 962 * Arbitrary Javascript to add to the HTML page. 963 */ 964 protected final String[] getScript() { 965 return script; 966 } 967 968 /** 969 * CSS style code. 970 * 971 * @see #HTMLDOC_style 972 * @return 973 * The CSS instructions to add to the HTML page. 974 */ 975 protected final String[] getStyle() { 976 return style; 977 } 978 979 /** 980 * Stylesheet import URLs. 981 * 982 * @see #HTMLDOC_stylesheet 983 * @return 984 * The link to the stylesheet of the HTML page. 985 */ 986 protected final String[] getStylesheet() { 987 return stylesheet; 988 } 989 990 /** 991 * HTML document template. 992 * 993 * @see #HTMLDOC_template 994 * @return 995 * The template to use for serializing the page. 996 */ 997 protected final HtmlDocTemplate getTemplate() { 998 return template; 999 } 1000 1001 /** 1002 * HTML widgets. 1003 * 1004 * @see #HTMLDOC_widgets 1005 * @return 1006 * Widgets defined on this serializers. 1007 */ 1008 protected final Map<String,HtmlWidget> getWidgets() { 1009 return widgets; 1010 } 1011 1012 //----------------------------------------------------------------------------------------------------------------- 1013 // Other methods 1014 //----------------------------------------------------------------------------------------------------------------- 1015 1016 @Override /* Context */ 1017 public OMap toMap() { 1018 return super.toMap() 1019 .a("HtmlDocSerializer", new DefaultFilteringOMap() 1020 .a("header", header) 1021 .a("nav", nav) 1022 .a("navlinks", navlinks) 1023 .a("aside", aside) 1024 .a("asideFloat", asideFloat) 1025 .a("footer", footer) 1026 .a("style", style) 1027 .a("head", head) 1028 .a("stylesheet", stylesheet) 1029 .a("nowrap", nowrap) 1030 .a("template", template) 1031 .a("noResultsMessage", noResultsMessage) 1032 .a("widgets", widgets.keySet()) 1033 ); 1034 } 1035}