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