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