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