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