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.collections.JsonMap.*; 020import static org.apache.juneau.common.utils.ThrowableUtils.*; 021import static org.apache.juneau.common.utils.Utils.*; 022import static org.apache.juneau.internal.CollectionUtils.*; 023 024import java.lang.annotation.*; 025import java.nio.charset.*; 026import java.util.*; 027import java.util.function.*; 028import java.util.regex.*; 029 030import org.apache.juneau.*; 031import org.apache.juneau.collections.*; 032import org.apache.juneau.common.utils.*; 033import org.apache.juneau.internal.*; 034import org.apache.juneau.svl.*; 035import org.apache.juneau.utils.*; 036import org.apache.juneau.xml.*; 037 038/** 039 * Serializes POJOs to HTTP responses as HTML documents. 040 * 041 * <h5 class='topic'>Media types</h5> 042 * <p> 043 * Handles <c>Accept</c> types: <bc>text/html</bc> 044 * <p> 045 * Produces <c>Content-Type</c> types: <bc>text/html</bc> 046 * 047 * <h5 class='topic'>Description</h5> 048 * <p> 049 * Same as {@link HtmlSerializer}, except wraps the response in <code><xt><html></code>, 050 * <code><xt><head></code>, and <code><xt><body></code> tags so that it can be rendered in a browser. 051 * 052 * <p> 053 * Configurable properties are typically specified via <ja>@HtmlDocConfig</ja>. 054 * 055 * <h5 class='section'>Example:</h5> 056 * <p class='bjava'> 057 * <ja>@Rest</ja>( 058 * messages=<js>"nls/AddressBookResource"</js>, 059 * title=<js>"$L{title}"</js>, 060 * description=<js>"$L{description}"</js> 061 * ) 062 * <ja>@HtmlDocConfig</ja>( 063 * navlinks={ 064 * <js>"api: servlet:/api"</js>, 065 * <js>"doc: doc"</js> 066 * } 067 * ) 068 * <jk>public class</jk> AddressBookResource <jk>extends</jk> BasicRestServlet { 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 * <h5 class='section'>Notes:</h5><ul> 078 * <li class='note'>This class is thread safe and reusable. 079 * </ul> 080 * 081 * <h5 class='section'>See Also:</h5><ul> 082 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HtmlBasics">HTML Basics</a> 083 084 * </ul> 085 */ 086public class HtmlDocSerializer extends HtmlStrippedDocSerializer { 087 088 //------------------------------------------------------------------------------------------------------------------- 089 // Static 090 //------------------------------------------------------------------------------------------------------------------- 091 092 private static final String[] EMPTY_ARRAY = {}; 093 094 /** Default serializer, all default settings. */ 095 public static final HtmlDocSerializer DEFAULT = new HtmlDocSerializer(create()); 096 097 /** 098 * Creates a new builder for this object. 099 * 100 * @return A new builder. 101 */ 102 public static Builder create() { 103 return new Builder(); 104 } 105 106 //------------------------------------------------------------------------------------------------------------------- 107 // Builder 108 //------------------------------------------------------------------------------------------------------------------- 109 110 /** 111 * Builder class. 112 */ 113 public static class Builder extends HtmlStrippedDocSerializer.Builder { 114 115 private static final Cache<HashKey,HtmlDocSerializer> CACHE = Cache.of(HashKey.class, HtmlDocSerializer.class).build(); 116 117 List<String> aside, footer, head, header, nav, navlinks, script, style, stylesheet; 118 AsideFloat asideFloat; 119 String noResultsMessage; 120 boolean nowrap, resolveBodyVars; 121 Class<? extends HtmlDocTemplate> template; 122 List<Class<? extends HtmlWidget>> widgets; 123 124 /** 125 * Constructor, default settings. 126 */ 127 protected Builder() { 128 produces("text/html"); 129 accept("text/html"); 130 asideFloat = AsideFloat.RIGHT; 131 noResultsMessage = "<p>no results</p>"; 132 template = BasicHtmlDocTemplate.class; 133 } 134 135 /** 136 * Copy constructor. 137 * 138 * @param copyFrom The bean to copy from. 139 */ 140 protected Builder(HtmlDocSerializer copyFrom) { 141 super(copyFrom); 142 aside = copy(copyFrom.aside); 143 footer = copy(copyFrom.footer); 144 head = copy(copyFrom.head); 145 header = copy(copyFrom.header); 146 nav = copy(copyFrom.nav); 147 navlinks = copy(copyFrom.navlinks); 148 script = copy(copyFrom.script); 149 style = copy(copyFrom.style); 150 stylesheet = copy(copyFrom.stylesheet); 151 asideFloat = copyFrom.asideFloat; 152 noResultsMessage = copyFrom.noResultsMessage; 153 nowrap = copyFrom.nowrap; 154 resolveBodyVars = copyFrom.resolveBodyVars; 155 template = copyFrom.template; 156 widgets = copy(copyFrom.widgets); 157 } 158 159 /** 160 * Copy constructor. 161 * 162 * @param copyFrom The builder to copy from. 163 */ 164 protected Builder(Builder copyFrom) { 165 super(copyFrom); 166 aside = copy(copyFrom.aside); 167 footer = copy(copyFrom.footer); 168 head = copy(copyFrom.head); 169 header = copy(copyFrom.header); 170 nav = copy(copyFrom.nav); 171 navlinks = copy(copyFrom.navlinks); 172 script = copy(copyFrom.script); 173 style = copy(copyFrom.style); 174 stylesheet = copy(copyFrom.stylesheet); 175 asideFloat = copyFrom.asideFloat; 176 noResultsMessage = copyFrom.noResultsMessage; 177 nowrap = copyFrom.nowrap; 178 resolveBodyVars = copyFrom.resolveBodyVars; 179 template = copyFrom.template; 180 widgets = copy(copyFrom.widgets); 181 } 182 183 @Override /* Context.Builder */ 184 public Builder copy() { 185 return new Builder(this); 186 } 187 188 @Override /* Context.Builder */ 189 public HtmlDocSerializer build() { 190 return cache(CACHE).build(HtmlDocSerializer.class); 191 } 192 193 @Override /* Context.Builder */ 194 public HashKey hashKey() { 195 return HashKey.of( 196 super.hashKey(), 197 aside, 198 footer, 199 head, 200 header, 201 nav, 202 navlinks, 203 script, 204 style, 205 stylesheet, 206 asideFloat, 207 noResultsMessage, 208 nowrap, 209 resolveBodyVars, 210 template, 211 widgets 212 ); 213 } 214 215 //----------------------------------------------------------------------------------------------------------------- 216 // Properties 217 //----------------------------------------------------------------------------------------------------------------- 218 219 /** 220 * Aside section contents. 221 * 222 * <p> 223 * Allows you to specify the contents of the aside section on the HTML page. 224 * The aside section floats on the right of the page for providing content supporting the serialized content of 225 * the page. 226 * 227 * <p> 228 * By default, the aside section is empty. 229 * 230 * <h5 class='section'>Example:</h5> 231 * <p class='bjava'> 232 * WriterSerializer <jv>serializer</jv> = HtmlDocSerializer 233 * .<jsm>create</jsm>() 234 * .aside( 235 * <js>"<ul>"</js>, 236 * <js>" <li>Item 1"</js>, 237 * <js>" <li>Item 2"</js>, 238 * <js>" <li>Item 3"</js>, 239 * <js>"</ul>"</js> 240 * ) 241 * .build(); 242 * </p> 243 * 244 * <h5 class='section'>Notes:</h5><ul> 245 * <li class='note'> 246 * Format: HTML 247 * <li class='note'> 248 * Supports <a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerSvlVariables">SVL Variables</a> 249 * (e.g. <js>"$L{my.localized.variable}"</js>). 250 * <li class='note'> 251 * A value of <js>"NONE"</js> can be used to force no value. 252 * <li class='note'> 253 * The parent value can be included by adding the literal <js>"INHERIT"</js> as a value. 254 * <li class='note'> 255 * Multiple values are combined with newlines into a single string. 256 * <li class='note'> 257 * On methods, this value is inherited from the <ja>@HtmlDocConfig</ja> annotation on the servlet/resource class. 258 * <li class='note'> 259 * On servlet/resource classes, this value is inherited from the <ja>@HtmlDocConfig</ja> annotation on the 260 * parent class. 261 * </ul> 262 * 263 * @param value 264 * The new value for this property. 265 * @return This object. 266 */ 267 public Builder aside(String...value) { 268 aside = merge(aside, value); 269 return this; 270 } 271 272 /** 273 * Returns the list of aside section contents. 274 * 275 * <p> 276 * Gives access to the inner list if you need to make more than simple additions via {@link #aside(String...)}. 277 * 278 * @return The list of aside section contents. 279 * @see #aside(String...) 280 */ 281 public List<String> aside() { 282 if (aside == null) 283 aside = list(); 284 return aside; 285 } 286 287 /** 288 * Float aside section contents. 289 * 290 * <p> 291 * Allows you to position the aside contents of the page around the main contents. 292 * 293 * <p> 294 * By default, the aside section is floated to the right. 295 * 296 * <h5 class='section'>Example:</h5> 297 * <p class='bjava'> 298 * WriterSerializer <jv>serializer</jv> = HtmlDocSerializer 299 * .<jsm>create</jsm>() 300 * .aside( 301 * <js>"<ul>"</js>, 302 * <js>" <li>Item 1"</js>, 303 * <js>" <li>Item 2"</js>, 304 * <js>" <li>Item 3"</js>, 305 * <js>"</ul>"</js> 306 * ) 307 * .asideFloat(<jsf>RIGHT</jsf>) 308 * .build(); 309 * </p> 310 * 311 * @param value 312 * The new value for this property. 313 * @return This object. 314 */ 315 public Builder asideFloat(AsideFloat value) { 316 asideFloat = value; 317 return this; 318 } 319 320 /** 321 * Footer section contents. 322 * 323 * <p> 324 * Allows you to specify the contents of the footer section on the HTML page. 325 * 326 * <p> 327 * By default, the footer section is empty. 328 * 329 * <h5 class='section'>Example:</h5> 330 * <p class='bjava'> 331 * WriterSerializer <jv>serializer</jv> = HtmlDocSerializer 332 * .<jsm>create</jsm>() 333 * .footer( 334 * <js>"<b>This interface is great!</b>"</js> 335 * ) 336 * .build(); 337 * </p> 338 * 339 * @param value 340 * The new value for this property. 341 * @return This object. 342 */ 343 public Builder footer(String...value) { 344 footer = merge(footer, value); 345 return this; 346 } 347 348 /** 349 * Returns the list of footer section contents. 350 * 351 * <p> 352 * Gives access to the inner list if you need to make more than simple additions via {@link #footer(String...)}. 353 * 354 * @return The list of footer section contents. 355 * @see #footer(String...) 356 */ 357 public List<String> footer() { 358 if (footer == null) 359 footer = list(); 360 return footer; 361 } 362 363 /** 364 * Additional head section content. 365 * 366 * <p> 367 * Adds the specified HTML content to the head section of the page. 368 * 369 * <h5 class='section'>Example:</h5> 370 * <p class='bjava'> 371 * WriterSerializer <jv>serializer</jv> = HtmlDocSerializer 372 * .<jsm>create</jsm>() 373 * .head( 374 * <js>"<link rel='icon' href='$U{servlet:/htdocs/mypageicon.ico}'>"</js> 375 * ) 376 * .build(); 377 * </p> 378 * 379 * @param value 380 * The new value for this property. 381 * @return This object. 382 */ 383 public Builder head(String...value) { 384 head = merge(head, value); 385 return this; 386 } 387 388 /** 389 * Returns the list of head section contents. 390 * 391 * <p> 392 * Gives access to the inner list if you need to make more than simple additions via {@link #head(String...)}. 393 * 394 * @return The list of head section contents. 395 * @see #head(String...) 396 */ 397 public List<String> head() { 398 if (head == null) 399 head = list(); 400 return head; 401 } 402 403 /** 404 * Header section contents. 405 * 406 * <p> 407 * Allows you to override the contents of the header section on the HTML page. 408 * The header section normally contains the title and description at the top of the page. 409 * 410 * <h5 class='section'>Example:</h5> 411 * <p class='bjava'> 412 * WriterSerializer <jv>serializer</jv> = HtmlDocSerializer 413 * .<jsm>create</jsm>() 414 * .header( 415 * <js>"<h1>My own header</h1>"</js> 416 * ) 417 * .build() 418 * </p> 419 * 420 * @param value 421 * The new value for this property. 422 * @return This object. 423 */ 424 public Builder header(String...value) { 425 header = merge(header, value); 426 return this; 427 } 428 429 /** 430 * Returns the list of header section contents. 431 * 432 * <p> 433 * Gives access to the inner list if you need to make more than simple additions via {@link #header(String...)}. 434 * 435 * @return The list of header section contents. 436 * @see #header(String...) 437 */ 438 public List<String> header() { 439 if (header == null) 440 header = list(); 441 return header; 442 } 443 444 /** 445 * Nav section contents. 446 * 447 * <p> 448 * Allows you to override the contents of the nav section on the HTML page. 449 * The nav section normally contains the page links at the top of the page. 450 * 451 * <h5 class='section'>Example:</h5> 452 * <p class='bjava'> 453 * WriterSerializer <jv>serializer</jv> = HtmlDocSerializer 454 * .<jsm>create</jsm>() 455 * .nav( 456 * <js>"<p class='special-navigation'>This is my special navigation content</p>"</js> 457 * ) 458 * .build() 459 * </p> 460 * 461 * <p> 462 * When this property is specified, the {@link Builder#navlinks(String...)} property is ignored. 463 * 464 * @param value 465 * The new value for this property. 466 * @return This object. 467 */ 468 public Builder nav(String...value) { 469 nav = merge(nav, value); 470 return this; 471 } 472 473 /** 474 * Returns the list of nav section contents. 475 * 476 * <p> 477 * Gives access to the inner list if you need to make more than simple additions via {@link #nav(String...)}. 478 * 479 * @return The list of nav section contents. 480 * @see #nav(String...) 481 */ 482 public List<String> nav() { 483 if (nav == null) 484 nav = list(); 485 return nav; 486 } 487 488 /** 489 * Page navigation links. 490 * 491 * <p> 492 * Adds a list of hyperlinks immediately under the title and description but above the content of the page. 493 * 494 * <p> 495 * This can be used to provide convenient hyperlinks when viewing the REST interface from a browser. 496 * 497 * <p> 498 * The value is an array of strings with two possible values: 499 * <ul> 500 * <li>A key-value pair representing a hyperlink label and href: 501 * <br><js>"google: http://google.com"</js> 502 * <li>Arbitrary HTML. 503 * </ul> 504 * 505 * <p> 506 * Relative URLs are considered relative to the servlet path. 507 * For example, if the servlet path is <js>"http://localhost/myContext/myServlet"</js>, and the 508 * URL is <js>"foo"</js>, the link becomes <js>"http://localhost/myContext/myServlet/foo"</js>. 509 * Absolute (<js>"/myOtherContext/foo"</js>) and fully-qualified (<js>"http://localhost2/foo"</js>) URLs 510 * can also be used in addition to various other protocols specified by {@link UriResolver} such as 511 * <js>"servlet:/..."</js>. 512 * 513 * <h5 class='section'>Example:</h5> 514 * <p class='bjava'> 515 * WriterSerializer <jv>serializer</jv> = HtmlDocSerializer 516 * .<jsm>create</jsm>() 517 * .navlinks( 518 * <js>"api: servlet:/api"</js>, 519 * <js>"stats: servlet:/stats"</js>, 520 * <js>"doc: doc"</js> 521 * ) 522 * .build(); 523 * </p> 524 * 525 * @param value 526 * The new value for this property. 527 * @return This object. 528 */ 529 public Builder navlinks(String...value) { 530 navlinks = mergeNavLinks(navlinks, value); 531 return this; 532 } 533 534 /** 535 * Returns the list of navlinks section contents. 536 * 537 * <p> 538 * Gives access to the inner list if you need to make more than simple additions via {@link #navlinks(String...)}. 539 * 540 * @return The list of navlinks section contents. 541 * @see #navlinks(String...) 542 */ 543 public List<String> navlinks() { 544 if (navlinks == null) 545 navlinks = list(); 546 return navlinks; 547 } 548 549 /** 550 * No-results message. 551 * 552 * <p> 553 * Allows you to specify the string message used when trying to serialize an empty array or empty list. 554 * 555 * <h5 class='section'>Example:</h5> 556 * <p class='bjava'> 557 * WriterSerializer <jv>serializer</jv> = HtmlDocSerializer 558 * .<jsm>create</jsm>() 559 * .noResultsMessage(<js>"<b>This interface is great!</b>"</js>) 560 * .build(); 561 * </p> 562 * 563 * <p> 564 * A value of <js>"NONE"</js> can be used to represent no value to differentiate it from an empty string. 565 * 566 * @param value 567 * The new value for this property. 568 * @return This object. 569 */ 570 public Builder noResultsMessage(String value) { 571 noResultsMessage = value; 572 return this; 573 } 574 575 /** 576 * Prevent word wrap on page. 577 * 578 * <p> 579 * Adds <js>"* {white-space:nowrap}"</js> to the CSS instructions on the page to prevent word wrapping. 580 * 581 * @return This object. 582 */ 583 public Builder nowrap() { 584 return nowrap(true); 585 } 586 587 /** 588 * Same as {@link #nowrap()} but allows you to explicitly specify the boolean value. 589 * 590 * @param value 591 * The new value for this property. 592 * @return This object. 593 * @see #nowrap() 594 */ 595 public Builder nowrap(boolean value) { 596 nowrap = value; 597 return this; 598 } 599 600 /** 601 * Resolve $ variables in serialized POJO. 602 * 603 * @return This object. 604 */ 605 public Builder resolveBodyVars() { 606 return resolveBodyVars(true); 607 } 608 609 /** 610 * Same as {@link #resolveBodyVars()} but allows you to explicitly specify the boolean value. 611 * 612 * @param value 613 * The new value for this property. 614 * @return This object. 615 * @see #nowrap() 616 */ 617 public Builder resolveBodyVars(boolean value) { 618 resolveBodyVars = value; 619 return this; 620 } 621 622 /** 623 * Adds the specified Javascript code to the HTML page. 624 * 625 * <p> 626 * A shortcut on <ja>@Rest</ja> is also provided for this setting: 627 * <p class='bjava'> 628 * WriterSerializer <jv>serializer</jv> = HtmlDocSerializer 629 * .<jsm>create</jsm>() 630 * .script(<js>"alert('hello!');"</js>) 631 * .build(); 632 * </p> 633 * 634 * @param value 635 * The value to add to this property. 636 * @return This object. 637 */ 638 public Builder script(String...value) { 639 script = merge(script, value); 640 return this; 641 } 642 643 /** 644 * Returns the list of page script contents. 645 * 646 * <p> 647 * Gives access to the inner list if you need to make more than simple additions via {@link #script(String...)}. 648 * 649 * @return The list of page script contents. 650 * @see #script(String...) 651 */ 652 public List<String> script() { 653 if (script == null) 654 script = list(); 655 return script; 656 } 657 658 /** 659 * Adds the specified CSS instructions to the HTML page. 660 * 661 * <p class='bjava'> 662 * WriterSerializer <jv>serializer</jv> = HtmlDocSerializer 663 * .<jsm>create</jsm>() 664 * .style( 665 * <js>"h3 { color: red; }"</js>, 666 * <js>"h5 { font-weight: bold; }"</js> 667 * ) 668 * .build(); 669 * </p> 670 * 671 * @param value 672 * The value to add to this property. 673 * @return This object. 674 */ 675 public Builder style(String...value) { 676 style = merge(style, value); 677 return this; 678 } 679 680 /** 681 * Returns the list of page style contents. 682 * 683 * <p> 684 * Gives access to the inner list if you need to make more than simple additions via {@link #style(String...)}. 685 * 686 * @return The list of page style contents. 687 * @see #style(String...) 688 */ 689 public List<String> style() { 690 if (style == null) 691 style = list(); 692 return style; 693 } 694 695 /** 696 * Adds to the list of stylesheet URLs. 697 * 698 * <p> 699 * Note that this stylesheet is controlled by the <code><ja>@Rest</ja>.stylesheet()</code> annotation. 700 * 701 * @param value 702 * The value to add to this property. 703 * @return This object. 704 */ 705 public Builder stylesheet(String...value) { 706 stylesheet = merge(stylesheet, value); 707 return this; 708 } 709 710 /** 711 * Returns the list of stylesheet URLs. 712 * 713 * <p> 714 * Gives access to the inner list if you need to make more than simple additions via {@link #stylesheet(String...)}. 715 * 716 * @return The list of stylesheet URLs. 717 * @see #stylesheet(String...) 718 */ 719 public List<String> stylesheet() { 720 if (stylesheet == null) 721 stylesheet = list(); 722 return stylesheet; 723 } 724 725 /** 726 * HTML document template. 727 * 728 * <p> 729 * Specifies the template to use for serializing the page. 730 * 731 * <p> 732 * By default, the {@link BasicHtmlDocTemplate} class is used to construct the contents of the HTML page, but 733 * can be overridden with your own custom implementation class. 734 * 735 * <h5 class='section'>Example:</h5> 736 * <p class='bjava'> 737 * WriterSerializer <jv>serializer</jv> = HtmlDocSerializer 738 * .<jsm>create</jsm>() 739 * .template(MySpecialDocTemplate.<jk>class</jk>) 740 * .build(); 741 * </p> 742 * 743 * @param value 744 * The new value for this property. 745 * @return This object. 746 */ 747 public Builder template(Class<? extends HtmlDocTemplate> value) { 748 template = value; 749 return this; 750 } 751 752 /** 753 * HTML Widgets. 754 * 755 * <p> 756 * Defines widgets that can be used in conjunction with string variables of the form <js>"$W{name}"</js>to quickly 757 * generate arbitrary replacement text. 758 * 759 * Widgets resolve the following variables: 760 * <ul class='spaced-list'> 761 * <li><js>"$W{name}"</js> - Contents returned by {@link HtmlWidget#getHtml(VarResolverSession)}. 762 * <li><js>"$W{name.script}"</js> - Contents returned by {@link HtmlWidget#getScript(VarResolverSession)}. 763 * <br>The script contents are automatically inserted into the <xt><head/script></xt> section 764 * in the HTML page. 765 * <li><js>"$W{name.style}"</js> - Contents returned by {@link HtmlWidget#getStyle(VarResolverSession)}. 766 * <br>The styles contents are automatically inserted into the <xt><head/style></xt> section 767 * in the HTML page. 768 * </ul> 769 * 770 * <p> 771 * The following examples shows how to associate a widget with a REST method and then have it rendered in the links 772 * and aside section of the page: 773 * 774 * <p class='bjava'> 775 * WriterSerializer <jv>serializer</jv> = HtmlDocSerializer 776 * .<jsm>create</jsm>() 777 * .widgets( 778 * MyWidget.<jk>class</jk> 779 * ) 780 * .navlinks( 781 * <js>"$W{MyWidget}"</js> 782 * ) 783 * .aside( 784 * <js>"Check out this widget: $W{MyWidget}"</js> 785 * ) 786 * .build(); 787 * </p> 788 * 789 * <h5 class='section'>Notes:</h5><ul> 790 * <li class='note'> 791 * Widgets are inherited from super classes, but can be overridden by reusing the widget name. 792 * </ul> 793 * 794 * <h5 class='section'>See Also:</h5><ul> 795 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HtmlWidgets">Widgets</a> 796 * </ul> 797 * 798 * @param values The values to add to this setting. 799 * @return This object. 800 */ 801 @SuppressWarnings("unchecked") 802 public Builder widgets(Class<? extends HtmlWidget>...values) { 803 addAll(widgets(), values); 804 return this; 805 } 806 807 /** 808 * Returns the list of page widgets. 809 * 810 * <p> 811 * Gives access to the inner list if you need to make more than simple additions via {@link #widgets(Class...)}. 812 * 813 * @return The list of page widgets. 814 * @see #widgets(Class...) 815 */ 816 public List<Class<? extends HtmlWidget>> widgets() { 817 if (widgets == null) 818 widgets = list(); 819 return widgets; 820 } 821 @Override /* Overridden from Builder */ 822 public Builder annotations(Annotation...values) { 823 super.annotations(values); 824 return this; 825 } 826 827 @Override /* Overridden from Builder */ 828 public Builder apply(AnnotationWorkList work) { 829 super.apply(work); 830 return this; 831 } 832 833 @Override /* Overridden from Builder */ 834 public Builder applyAnnotations(Object...from) { 835 super.applyAnnotations(from); 836 return this; 837 } 838 839 @Override /* Overridden from Builder */ 840 public Builder applyAnnotations(Class<?>...from) { 841 super.applyAnnotations(from); 842 return this; 843 } 844 845 @Override /* Overridden from Builder */ 846 public Builder cache(Cache<HashKey,? extends org.apache.juneau.Context> value) { 847 super.cache(value); 848 return this; 849 } 850 851 @Override /* Overridden from Builder */ 852 public Builder debug() { 853 super.debug(); 854 return this; 855 } 856 857 @Override /* Overridden from Builder */ 858 public Builder debug(boolean value) { 859 super.debug(value); 860 return this; 861 } 862 863 @Override /* Overridden from Builder */ 864 public Builder impl(Context value) { 865 super.impl(value); 866 return this; 867 } 868 869 @Override /* Overridden from Builder */ 870 public Builder type(Class<? extends org.apache.juneau.Context> value) { 871 super.type(value); 872 return this; 873 } 874 875 @Override /* Overridden from Builder */ 876 public Builder beanClassVisibility(Visibility value) { 877 super.beanClassVisibility(value); 878 return this; 879 } 880 881 @Override /* Overridden from Builder */ 882 public Builder beanConstructorVisibility(Visibility value) { 883 super.beanConstructorVisibility(value); 884 return this; 885 } 886 887 @Override /* Overridden from Builder */ 888 public Builder beanContext(BeanContext value) { 889 super.beanContext(value); 890 return this; 891 } 892 893 @Override /* Overridden from Builder */ 894 public Builder beanContext(BeanContext.Builder value) { 895 super.beanContext(value); 896 return this; 897 } 898 899 @Override /* Overridden from Builder */ 900 public Builder beanDictionary(java.lang.Class<?>...values) { 901 super.beanDictionary(values); 902 return this; 903 } 904 905 @Override /* Overridden from Builder */ 906 public Builder beanFieldVisibility(Visibility value) { 907 super.beanFieldVisibility(value); 908 return this; 909 } 910 911 @Override /* Overridden from Builder */ 912 public Builder beanInterceptor(Class<?> on, Class<? extends org.apache.juneau.swap.BeanInterceptor<?>> value) { 913 super.beanInterceptor(on, value); 914 return this; 915 } 916 917 @Override /* Overridden from Builder */ 918 public Builder beanMapPutReturnsOldValue() { 919 super.beanMapPutReturnsOldValue(); 920 return this; 921 } 922 923 @Override /* Overridden from Builder */ 924 public Builder beanMethodVisibility(Visibility value) { 925 super.beanMethodVisibility(value); 926 return this; 927 } 928 929 @Override /* Overridden from Builder */ 930 public Builder beanProperties(Map<String,Object> values) { 931 super.beanProperties(values); 932 return this; 933 } 934 935 @Override /* Overridden from Builder */ 936 public Builder beanProperties(Class<?> beanClass, String properties) { 937 super.beanProperties(beanClass, properties); 938 return this; 939 } 940 941 @Override /* Overridden from Builder */ 942 public Builder beanProperties(String beanClassName, String properties) { 943 super.beanProperties(beanClassName, properties); 944 return this; 945 } 946 947 @Override /* Overridden from Builder */ 948 public Builder beanPropertiesExcludes(Map<String,Object> values) { 949 super.beanPropertiesExcludes(values); 950 return this; 951 } 952 953 @Override /* Overridden from Builder */ 954 public Builder beanPropertiesExcludes(Class<?> beanClass, String properties) { 955 super.beanPropertiesExcludes(beanClass, properties); 956 return this; 957 } 958 959 @Override /* Overridden from Builder */ 960 public Builder beanPropertiesExcludes(String beanClassName, String properties) { 961 super.beanPropertiesExcludes(beanClassName, properties); 962 return this; 963 } 964 965 @Override /* Overridden from Builder */ 966 public Builder beanPropertiesReadOnly(Map<String,Object> values) { 967 super.beanPropertiesReadOnly(values); 968 return this; 969 } 970 971 @Override /* Overridden from Builder */ 972 public Builder beanPropertiesReadOnly(Class<?> beanClass, String properties) { 973 super.beanPropertiesReadOnly(beanClass, properties); 974 return this; 975 } 976 977 @Override /* Overridden from Builder */ 978 public Builder beanPropertiesReadOnly(String beanClassName, String properties) { 979 super.beanPropertiesReadOnly(beanClassName, properties); 980 return this; 981 } 982 983 @Override /* Overridden from Builder */ 984 public Builder beanPropertiesWriteOnly(Map<String,Object> values) { 985 super.beanPropertiesWriteOnly(values); 986 return this; 987 } 988 989 @Override /* Overridden from Builder */ 990 public Builder beanPropertiesWriteOnly(Class<?> beanClass, String properties) { 991 super.beanPropertiesWriteOnly(beanClass, properties); 992 return this; 993 } 994 995 @Override /* Overridden from Builder */ 996 public Builder beanPropertiesWriteOnly(String beanClassName, String properties) { 997 super.beanPropertiesWriteOnly(beanClassName, properties); 998 return this; 999 } 1000 1001 @Override /* Overridden from Builder */ 1002 public Builder beansRequireDefaultConstructor() { 1003 super.beansRequireDefaultConstructor(); 1004 return this; 1005 } 1006 1007 @Override /* Overridden from Builder */ 1008 public Builder beansRequireSerializable() { 1009 super.beansRequireSerializable(); 1010 return this; 1011 } 1012 1013 @Override /* Overridden from Builder */ 1014 public Builder beansRequireSettersForGetters() { 1015 super.beansRequireSettersForGetters(); 1016 return this; 1017 } 1018 1019 @Override /* Overridden from Builder */ 1020 public Builder dictionaryOn(Class<?> on, java.lang.Class<?>...values) { 1021 super.dictionaryOn(on, values); 1022 return this; 1023 } 1024 1025 @Override /* Overridden from Builder */ 1026 public Builder disableBeansRequireSomeProperties() { 1027 super.disableBeansRequireSomeProperties(); 1028 return this; 1029 } 1030 1031 @Override /* Overridden from Builder */ 1032 public Builder disableIgnoreMissingSetters() { 1033 super.disableIgnoreMissingSetters(); 1034 return this; 1035 } 1036 1037 @Override /* Overridden from Builder */ 1038 public Builder disableIgnoreTransientFields() { 1039 super.disableIgnoreTransientFields(); 1040 return this; 1041 } 1042 1043 @Override /* Overridden from Builder */ 1044 public Builder disableIgnoreUnknownNullBeanProperties() { 1045 super.disableIgnoreUnknownNullBeanProperties(); 1046 return this; 1047 } 1048 1049 @Override /* Overridden from Builder */ 1050 public Builder disableInterfaceProxies() { 1051 super.disableInterfaceProxies(); 1052 return this; 1053 } 1054 1055 @Override /* Overridden from Builder */ 1056 public <T> Builder example(Class<T> pojoClass, T o) { 1057 super.example(pojoClass, o); 1058 return this; 1059 } 1060 1061 @Override /* Overridden from Builder */ 1062 public <T> Builder example(Class<T> pojoClass, String json) { 1063 super.example(pojoClass, json); 1064 return this; 1065 } 1066 1067 @Override /* Overridden from Builder */ 1068 public Builder findFluentSetters() { 1069 super.findFluentSetters(); 1070 return this; 1071 } 1072 1073 @Override /* Overridden from Builder */ 1074 public Builder findFluentSetters(Class<?> on) { 1075 super.findFluentSetters(on); 1076 return this; 1077 } 1078 1079 @Override /* Overridden from Builder */ 1080 public Builder ignoreInvocationExceptionsOnGetters() { 1081 super.ignoreInvocationExceptionsOnGetters(); 1082 return this; 1083 } 1084 1085 @Override /* Overridden from Builder */ 1086 public Builder ignoreInvocationExceptionsOnSetters() { 1087 super.ignoreInvocationExceptionsOnSetters(); 1088 return this; 1089 } 1090 1091 @Override /* Overridden from Builder */ 1092 public Builder ignoreUnknownBeanProperties() { 1093 super.ignoreUnknownBeanProperties(); 1094 return this; 1095 } 1096 1097 @Override /* Overridden from Builder */ 1098 public Builder ignoreUnknownEnumValues() { 1099 super.ignoreUnknownEnumValues(); 1100 return this; 1101 } 1102 1103 @Override /* Overridden from Builder */ 1104 public Builder implClass(Class<?> interfaceClass, Class<?> implClass) { 1105 super.implClass(interfaceClass, implClass); 1106 return this; 1107 } 1108 1109 @Override /* Overridden from Builder */ 1110 public Builder implClasses(Map<Class<?>,Class<?>> values) { 1111 super.implClasses(values); 1112 return this; 1113 } 1114 1115 @Override /* Overridden from Builder */ 1116 public Builder interfaceClass(Class<?> on, Class<?> value) { 1117 super.interfaceClass(on, value); 1118 return this; 1119 } 1120 1121 @Override /* Overridden from Builder */ 1122 public Builder interfaces(java.lang.Class<?>...value) { 1123 super.interfaces(value); 1124 return this; 1125 } 1126 1127 @Override /* Overridden from Builder */ 1128 public Builder locale(Locale value) { 1129 super.locale(value); 1130 return this; 1131 } 1132 1133 @Override /* Overridden from Builder */ 1134 public Builder mediaType(MediaType value) { 1135 super.mediaType(value); 1136 return this; 1137 } 1138 1139 @Override /* Overridden from Builder */ 1140 public Builder notBeanClasses(java.lang.Class<?>...values) { 1141 super.notBeanClasses(values); 1142 return this; 1143 } 1144 1145 @Override /* Overridden from Builder */ 1146 public Builder notBeanPackages(String...values) { 1147 super.notBeanPackages(values); 1148 return this; 1149 } 1150 1151 @Override /* Overridden from Builder */ 1152 public Builder propertyNamer(Class<? extends org.apache.juneau.PropertyNamer> value) { 1153 super.propertyNamer(value); 1154 return this; 1155 } 1156 1157 @Override /* Overridden from Builder */ 1158 public Builder propertyNamer(Class<?> on, Class<? extends org.apache.juneau.PropertyNamer> value) { 1159 super.propertyNamer(on, value); 1160 return this; 1161 } 1162 1163 @Override /* Overridden from Builder */ 1164 public Builder sortProperties() { 1165 super.sortProperties(); 1166 return this; 1167 } 1168 1169 @Override /* Overridden from Builder */ 1170 public Builder sortProperties(java.lang.Class<?>...on) { 1171 super.sortProperties(on); 1172 return this; 1173 } 1174 1175 @Override /* Overridden from Builder */ 1176 public Builder stopClass(Class<?> on, Class<?> value) { 1177 super.stopClass(on, value); 1178 return this; 1179 } 1180 1181 @Override /* Overridden from Builder */ 1182 public <T, S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction) { 1183 super.swap(normalClass, swappedClass, swapFunction); 1184 return this; 1185 } 1186 1187 @Override /* Overridden from Builder */ 1188 public <T, S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction, ThrowingFunction<S,T> unswapFunction) { 1189 super.swap(normalClass, swappedClass, swapFunction, unswapFunction); 1190 return this; 1191 } 1192 1193 @Override /* Overridden from Builder */ 1194 public Builder swaps(Object...values) { 1195 super.swaps(values); 1196 return this; 1197 } 1198 1199 @Override /* Overridden from Builder */ 1200 public Builder swaps(Class<?>...values) { 1201 super.swaps(values); 1202 return this; 1203 } 1204 1205 @Override /* Overridden from Builder */ 1206 public Builder timeZone(TimeZone value) { 1207 super.timeZone(value); 1208 return this; 1209 } 1210 1211 @Override /* Overridden from Builder */ 1212 public Builder typeName(Class<?> on, String value) { 1213 super.typeName(on, value); 1214 return this; 1215 } 1216 1217 @Override /* Overridden from Builder */ 1218 public Builder typePropertyName(String value) { 1219 super.typePropertyName(value); 1220 return this; 1221 } 1222 1223 @Override /* Overridden from Builder */ 1224 public Builder typePropertyName(Class<?> on, String value) { 1225 super.typePropertyName(on, value); 1226 return this; 1227 } 1228 1229 @Override /* Overridden from Builder */ 1230 public Builder useEnumNames() { 1231 super.useEnumNames(); 1232 return this; 1233 } 1234 1235 @Override /* Overridden from Builder */ 1236 public Builder useJavaBeanIntrospector() { 1237 super.useJavaBeanIntrospector(); 1238 return this; 1239 } 1240 1241 @Override /* Overridden from Builder */ 1242 public Builder detectRecursions() { 1243 super.detectRecursions(); 1244 return this; 1245 } 1246 1247 @Override /* Overridden from Builder */ 1248 public Builder detectRecursions(boolean value) { 1249 super.detectRecursions(value); 1250 return this; 1251 } 1252 1253 @Override /* Overridden from Builder */ 1254 public Builder ignoreRecursions() { 1255 super.ignoreRecursions(); 1256 return this; 1257 } 1258 1259 @Override /* Overridden from Builder */ 1260 public Builder ignoreRecursions(boolean value) { 1261 super.ignoreRecursions(value); 1262 return this; 1263 } 1264 1265 @Override /* Overridden from Builder */ 1266 public Builder initialDepth(int value) { 1267 super.initialDepth(value); 1268 return this; 1269 } 1270 1271 @Override /* Overridden from Builder */ 1272 public Builder maxDepth(int value) { 1273 super.maxDepth(value); 1274 return this; 1275 } 1276 1277 @Override /* Overridden from Builder */ 1278 public Builder accept(String value) { 1279 super.accept(value); 1280 return this; 1281 } 1282 1283 @Override /* Overridden from Builder */ 1284 public Builder addBeanTypes() { 1285 super.addBeanTypes(); 1286 return this; 1287 } 1288 1289 @Override /* Overridden from Builder */ 1290 public Builder addBeanTypes(boolean value) { 1291 super.addBeanTypes(value); 1292 return this; 1293 } 1294 1295 @Override /* Overridden from Builder */ 1296 public Builder addRootType() { 1297 super.addRootType(); 1298 return this; 1299 } 1300 1301 @Override /* Overridden from Builder */ 1302 public Builder addRootType(boolean value) { 1303 super.addRootType(value); 1304 return this; 1305 } 1306 1307 @Override /* Overridden from Builder */ 1308 public Builder keepNullProperties() { 1309 super.keepNullProperties(); 1310 return this; 1311 } 1312 1313 @Override /* Overridden from Builder */ 1314 public Builder keepNullProperties(boolean value) { 1315 super.keepNullProperties(value); 1316 return this; 1317 } 1318 1319 @Override /* Overridden from Builder */ 1320 public Builder listener(Class<? extends org.apache.juneau.serializer.SerializerListener> value) { 1321 super.listener(value); 1322 return this; 1323 } 1324 1325 @Override /* Overridden from Builder */ 1326 public Builder produces(String value) { 1327 super.produces(value); 1328 return this; 1329 } 1330 1331 @Override /* Overridden from Builder */ 1332 public Builder sortCollections() { 1333 super.sortCollections(); 1334 return this; 1335 } 1336 1337 @Override /* Overridden from Builder */ 1338 public Builder sortCollections(boolean value) { 1339 super.sortCollections(value); 1340 return this; 1341 } 1342 1343 @Override /* Overridden from Builder */ 1344 public Builder sortMaps() { 1345 super.sortMaps(); 1346 return this; 1347 } 1348 1349 @Override /* Overridden from Builder */ 1350 public Builder sortMaps(boolean value) { 1351 super.sortMaps(value); 1352 return this; 1353 } 1354 1355 @Override /* Overridden from Builder */ 1356 public Builder trimEmptyCollections() { 1357 super.trimEmptyCollections(); 1358 return this; 1359 } 1360 1361 @Override /* Overridden from Builder */ 1362 public Builder trimEmptyCollections(boolean value) { 1363 super.trimEmptyCollections(value); 1364 return this; 1365 } 1366 1367 @Override /* Overridden from Builder */ 1368 public Builder trimEmptyMaps() { 1369 super.trimEmptyMaps(); 1370 return this; 1371 } 1372 1373 @Override /* Overridden from Builder */ 1374 public Builder trimEmptyMaps(boolean value) { 1375 super.trimEmptyMaps(value); 1376 return this; 1377 } 1378 1379 @Override /* Overridden from Builder */ 1380 public Builder trimStrings() { 1381 super.trimStrings(); 1382 return this; 1383 } 1384 1385 @Override /* Overridden from Builder */ 1386 public Builder trimStrings(boolean value) { 1387 super.trimStrings(value); 1388 return this; 1389 } 1390 1391 @Override /* Overridden from Builder */ 1392 public Builder uriContext(UriContext value) { 1393 super.uriContext(value); 1394 return this; 1395 } 1396 1397 @Override /* Overridden from Builder */ 1398 public Builder uriRelativity(UriRelativity value) { 1399 super.uriRelativity(value); 1400 return this; 1401 } 1402 1403 @Override /* Overridden from Builder */ 1404 public Builder uriResolution(UriResolution value) { 1405 super.uriResolution(value); 1406 return this; 1407 } 1408 1409 @Override /* Overridden from Builder */ 1410 public Builder fileCharset(Charset value) { 1411 super.fileCharset(value); 1412 return this; 1413 } 1414 1415 @Override /* Overridden from Builder */ 1416 public Builder maxIndent(int value) { 1417 super.maxIndent(value); 1418 return this; 1419 } 1420 1421 @Override /* Overridden from Builder */ 1422 public Builder quoteChar(char value) { 1423 super.quoteChar(value); 1424 return this; 1425 } 1426 1427 @Override /* Overridden from Builder */ 1428 public Builder quoteCharOverride(char value) { 1429 super.quoteCharOverride(value); 1430 return this; 1431 } 1432 1433 @Override /* Overridden from Builder */ 1434 public Builder sq() { 1435 super.sq(); 1436 return this; 1437 } 1438 1439 @Override /* Overridden from Builder */ 1440 public Builder streamCharset(Charset value) { 1441 super.streamCharset(value); 1442 return this; 1443 } 1444 1445 @Override /* Overridden from Builder */ 1446 public Builder useWhitespace() { 1447 super.useWhitespace(); 1448 return this; 1449 } 1450 1451 @Override /* Overridden from Builder */ 1452 public Builder useWhitespace(boolean value) { 1453 super.useWhitespace(value); 1454 return this; 1455 } 1456 1457 @Override /* Overridden from Builder */ 1458 public Builder ws() { 1459 super.ws(); 1460 return this; 1461 } 1462 1463 @Override /* Overridden from Builder */ 1464 public Builder addBeanTypesXml() { 1465 super.addBeanTypesXml(); 1466 return this; 1467 } 1468 1469 @Override /* Overridden from Builder */ 1470 public Builder addBeanTypesXml(boolean value) { 1471 super.addBeanTypesXml(value); 1472 return this; 1473 } 1474 1475 @Override /* Overridden from Builder */ 1476 public Builder addNamespaceUrisToRoot() { 1477 super.addNamespaceUrisToRoot(); 1478 return this; 1479 } 1480 1481 @Override /* Overridden from Builder */ 1482 public Builder addNamespaceUrisToRoot(boolean value) { 1483 super.addNamespaceUrisToRoot(value); 1484 return this; 1485 } 1486 1487 @Override /* Overridden from Builder */ 1488 public Builder defaultNamespace(Namespace value) { 1489 super.defaultNamespace(value); 1490 return this; 1491 } 1492 1493 @Override /* Overridden from Builder */ 1494 public Builder disableAutoDetectNamespaces() { 1495 super.disableAutoDetectNamespaces(); 1496 return this; 1497 } 1498 1499 @Override /* Overridden from Builder */ 1500 public Builder disableAutoDetectNamespaces(boolean value) { 1501 super.disableAutoDetectNamespaces(value); 1502 return this; 1503 } 1504 1505 @Override /* Overridden from Builder */ 1506 public Builder enableNamespaces() { 1507 super.enableNamespaces(); 1508 return this; 1509 } 1510 1511 @Override /* Overridden from Builder */ 1512 public Builder enableNamespaces(boolean value) { 1513 super.enableNamespaces(value); 1514 return this; 1515 } 1516 1517 @Override /* Overridden from Builder */ 1518 public Builder namespaces(Namespace...values) { 1519 super.namespaces(values); 1520 return this; 1521 } 1522 1523 @Override /* Overridden from Builder */ 1524 public Builder ns() { 1525 super.ns(); 1526 return this; 1527 } 1528 1529 @Override /* Overridden from Builder */ 1530 public Builder addBeanTypesHtml() { 1531 super.addBeanTypesHtml(); 1532 return this; 1533 } 1534 1535 @Override /* Overridden from Builder */ 1536 public Builder addBeanTypesHtml(boolean value) { 1537 super.addBeanTypesHtml(value); 1538 return this; 1539 } 1540 1541 @Override /* Overridden from Builder */ 1542 public Builder addKeyValueTableHeaders() { 1543 super.addKeyValueTableHeaders(); 1544 return this; 1545 } 1546 1547 @Override /* Overridden from Builder */ 1548 public Builder addKeyValueTableHeaders(boolean value) { 1549 super.addKeyValueTableHeaders(value); 1550 return this; 1551 } 1552 1553 @Override /* Overridden from Builder */ 1554 public Builder disableDetectLabelParameters() { 1555 super.disableDetectLabelParameters(); 1556 return this; 1557 } 1558 1559 @Override /* Overridden from Builder */ 1560 public Builder disableDetectLabelParameters(boolean value) { 1561 super.disableDetectLabelParameters(value); 1562 return this; 1563 } 1564 1565 @Override /* Overridden from Builder */ 1566 public Builder disableDetectLinksInStrings() { 1567 super.disableDetectLinksInStrings(); 1568 return this; 1569 } 1570 1571 @Override /* Overridden from Builder */ 1572 public Builder disableDetectLinksInStrings(boolean value) { 1573 super.disableDetectLinksInStrings(value); 1574 return this; 1575 } 1576 1577 @Override /* Overridden from Builder */ 1578 public Builder labelParameter(String value) { 1579 super.labelParameter(value); 1580 return this; 1581 } 1582 1583 @Override /* Overridden from Builder */ 1584 public Builder uriAnchorText(AnchorText value) { 1585 super.uriAnchorText(value); 1586 return this; 1587 } 1588 //----------------------------------------------------------------------------------------------------------------- 1589 // Helpers 1590 //----------------------------------------------------------------------------------------------------------------- 1591 1592 private static <T> List<T> copy(List<T> s) { 1593 return s == null || s.isEmpty() ? null : copyOf(s); 1594 } 1595 1596 private static <T> List<T> copy(T[] s) { 1597 return s.length == 0 ? null : list(s); 1598 } 1599 1600 private List<String> merge(List<String> old, String[] newValues) { 1601 List<String> x = Utils.listOfSize(newValues.length); 1602 for (String s : newValues) { 1603 if ("NONE".equals(s)) { 1604 if (old != null) 1605 old.clear(); 1606 } else if ("INHERIT".equals(s)) { 1607 if (old != null) 1608 x.addAll(old); 1609 } else { 1610 x.add(s); 1611 } 1612 } 1613 return x; 1614 } 1615 1616 private List<String> mergeNavLinks(List<String> old, String[] newValues) { 1617 List<String> x = Utils.listOfSize(newValues.length); 1618 for (String s : newValues) { 1619 if ("NONE".equals(s)) { 1620 if (old != null) 1621 old.clear(); 1622 } else if ("INHERIT".equals(s)) { 1623 if (old != null) 1624 x.addAll(old); 1625 } else if (s.indexOf('[') != -1 && INDEXED_LINK_PATTERN.matcher(s).matches()) { 1626 Matcher lm = INDEXED_LINK_PATTERN.matcher(s); 1627 lm.matches(); 1628 String key = lm.group(1); 1629 int index = Math.min(x.size(), Integer.parseInt(lm.group(2))); 1630 String remainder = lm.group(3); 1631 x.add(index, key.isEmpty() ? remainder : key + ":" + remainder); 1632 } else { 1633 x.add(s); 1634 } 1635 } 1636 return x; 1637 } 1638 1639 private static final Pattern INDEXED_LINK_PATTERN = Pattern.compile("(?s)(\\S*)\\[(\\d+)\\]\\:(.*)"); 1640 } 1641 1642 //------------------------------------------------------------------------------------------------------------------- 1643 // Instance 1644 //------------------------------------------------------------------------------------------------------------------- 1645 1646 final String[] style, stylesheet, script, navlinks, head, header, nav, aside, footer; 1647 final AsideFloat asideFloat; 1648 final String noResultsMessage; 1649 final boolean nowrap, resolveBodyVars; 1650 final Class<? extends HtmlDocTemplate> template; 1651 final List<Class<? extends HtmlWidget>> widgets; 1652 1653 private final HtmlWidgetMap widgetMap; 1654 private final HtmlWidget[] widgetArray; 1655 private final HtmlDocTemplate templateBean; 1656 1657 private volatile HtmlSchemaDocSerializer schemaSerializer; 1658 1659 /** 1660 * Constructor. 1661 * 1662 * @param builder The builder for this object. 1663 */ 1664 public HtmlDocSerializer(Builder builder) { 1665 super(builder); 1666 style = builder.style != null ? toArray(builder.style) : EMPTY_ARRAY; 1667 stylesheet = builder.stylesheet != null ? toArray(builder.stylesheet) : EMPTY_ARRAY; 1668 script = builder.script != null ? toArray(builder.script) : EMPTY_ARRAY; 1669 head = builder.head != null ? toArray(builder.head) : EMPTY_ARRAY; 1670 header = builder.header != null ? toArray(builder.header) : EMPTY_ARRAY; 1671 nav = builder.nav != null ? toArray(builder.nav) : EMPTY_ARRAY; 1672 aside = builder.aside != null ? toArray(builder.aside) : EMPTY_ARRAY; 1673 footer = builder.footer != null ? toArray(builder.footer) : EMPTY_ARRAY; 1674 navlinks = builder.navlinks != null ? toArray(builder.navlinks) : EMPTY_ARRAY; 1675 asideFloat = builder.asideFloat; 1676 noResultsMessage = builder.noResultsMessage; 1677 nowrap = builder.nowrap; 1678 resolveBodyVars = builder.resolveBodyVars; 1679 template = builder.template; 1680 widgets = builder.widgets == null ? Collections.emptyList() : copyOf(builder.widgets); 1681 1682 templateBean = newInstance(template); 1683 widgetMap = new HtmlWidgetMap(); 1684 widgets.stream().map(this::newInstance).forEach(x -> widgetMap.append(x)); 1685 widgetArray = Utils.array(widgetMap.values(), HtmlWidget.class); 1686 } 1687 1688 @Override /* Context */ 1689 public Builder copy() { 1690 return new Builder(this); 1691 } 1692 1693 @Override /* Context */ 1694 public HtmlDocSerializerSession.Builder createSession() { 1695 return HtmlDocSerializerSession.create(this); 1696 } 1697 1698 @Override /* Context */ 1699 public HtmlDocSerializerSession getSession() { 1700 return createSession().build(); 1701 } 1702 1703 @Override /* XmlSerializer */ 1704 public HtmlSerializer getSchemaSerializer() { 1705 if (schemaSerializer == null) 1706 schemaSerializer = HtmlSchemaDocSerializer.create().beanContext(getBeanContext()).build(); 1707 return schemaSerializer; 1708 } 1709 1710 //----------------------------------------------------------------------------------------------------------------- 1711 // Properties 1712 //----------------------------------------------------------------------------------------------------------------- 1713 1714 /** 1715 * Aside section contents. 1716 * 1717 * @see Builder#aside(String...) 1718 * @return 1719 * The overridden contents of the aside section on the HTML page. 1720 */ 1721 protected final String[] getAside() { 1722 return aside; 1723 } 1724 1725 /** 1726 * Float side section contents. 1727 * 1728 * @see Builder#asideFloat(AsideFloat) 1729 * @return 1730 * How to float the aside contents on the page. 1731 */ 1732 protected final AsideFloat getAsideFloat() { 1733 return asideFloat; 1734 } 1735 1736 /** 1737 * Footer section contents. 1738 * 1739 * @see Builder#footer(String...) 1740 * @return 1741 * The overridden contents of the footer section on the HTML page. 1742 */ 1743 protected final String[] getFooter() { 1744 return footer; 1745 } 1746 1747 /** 1748 * Additional head section content. 1749 * 1750 * @see Builder#head(String...) 1751 * @return 1752 * HTML content to add to the head section of the HTML page. 1753 */ 1754 protected final String[] getHead() { 1755 return head; 1756 } 1757 1758 /** 1759 * Header section contents. 1760 * 1761 * @see Builder#header(String...) 1762 * @return 1763 * The overridden contents of the header section on the HTML page. 1764 */ 1765 protected final String[] getHeader() { 1766 return header; 1767 } 1768 1769 /** 1770 * Nav section contents. 1771 * 1772 * @see Builder#nav(String...) 1773 * @return 1774 * The overridden contents of the nav section on the HTML page. 1775 */ 1776 protected final String[] getNav() { 1777 return nav; 1778 } 1779 1780 /** 1781 * Page navigation links. 1782 * 1783 * @see Builder#navlinks(String...) 1784 * @return 1785 * Navigation links to add to the HTML page. 1786 */ 1787 protected final String[] getNavlinks() { 1788 return navlinks; 1789 } 1790 1791 /** 1792 * No-results message. 1793 * 1794 * @see Builder#noResultsMessage(String) 1795 * @return 1796 * The message used when serializing an empty array or empty list. 1797 */ 1798 protected final String getNoResultsMessage() { 1799 return noResultsMessage; 1800 } 1801 1802 /** 1803 * Prevent word wrap on page. 1804 * 1805 * @see Builder#nowrap() 1806 * @return 1807 * <jk>true</jk> if <js>"* {white-space:nowrap}"</js> shoudl be added to the CSS instructions on the page to prevent word wrapping. 1808 */ 1809 protected final boolean isNowrap() { 1810 return nowrap; 1811 } 1812 1813 /** 1814 * Javascript code. 1815 * 1816 * @see Builder#script(String...) 1817 * @return 1818 * Arbitrary Javascript to add to the HTML page. 1819 */ 1820 protected final String[] getScript() { 1821 return script; 1822 } 1823 1824 /** 1825 * CSS style code. 1826 * 1827 * @see Builder#style(String...) 1828 * @return 1829 * The CSS instructions to add to the HTML page. 1830 */ 1831 protected final String[] getStyle() { 1832 return style; 1833 } 1834 1835 /** 1836 * Stylesheet import URLs. 1837 * 1838 * @see Builder#stylesheet(String...) 1839 * @return 1840 * The link to the stylesheet of the HTML page. 1841 */ 1842 protected final String[] getStylesheet() { 1843 return stylesheet; 1844 } 1845 1846 /** 1847 * HTML document template. 1848 * 1849 * @see Builder#template(Class) 1850 * @return 1851 * The template to use for serializing the page. 1852 */ 1853 protected final HtmlDocTemplate getTemplate() { 1854 return templateBean; 1855 } 1856 1857 /** 1858 * HTML widgets. 1859 * 1860 * @see Builder#widgets(Class...) 1861 * @return 1862 * Widgets defined on this serializers. 1863 */ 1864 protected final HtmlWidgetMap getWidgets() { 1865 return widgetMap; 1866 } 1867 1868 /** 1869 * Performs an action on all widgets defined on this serializer. 1870 * 1871 * @param action The action to perform. 1872 * @return This object. 1873 */ 1874 protected final HtmlDocSerializer forEachWidget(Consumer<HtmlWidget> action) { 1875 for (HtmlWidget w : widgetArray) 1876 action.accept(w); 1877 return this; 1878 } 1879 1880 //----------------------------------------------------------------------------------------------------------------- 1881 // Other methods 1882 //----------------------------------------------------------------------------------------------------------------- 1883 1884 private String[] toArray(List<String> x) { 1885 return x.toArray(new String[x.size()]); 1886 } 1887 1888 private <T> T newInstance(Class<T> c) { 1889 try { 1890 return c.getDeclaredConstructor().newInstance(); 1891 } catch (Exception e) { 1892 throw asRuntimeException(e); 1893 } 1894 } 1895 1896 @Override /* Context */ 1897 protected JsonMap properties() { 1898 return filteredMap() 1899 .append("header", header) 1900 .append("nav", nav) 1901 .append("navlinks", navlinks) 1902 .append("aside", aside) 1903 .append("asideFloat", asideFloat) 1904 .append("footer", footer) 1905 .append("style", style) 1906 .append("head", head) 1907 .append("stylesheet", stylesheet) 1908 .append("nowrap", nowrap) 1909 .append("template", template) 1910 .append("noResultsMessage", noResultsMessage) 1911 .append("widgets", widgets); 1912 } 1913}