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