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 java.util.*;
016
017import org.apache.juneau.*;
018import org.apache.juneau.annotation.*;
019import org.apache.juneau.html.annotation.*;
020import org.apache.juneau.serializer.*;
021import org.apache.juneau.svl.*;
022
023/**
024 * Serializes POJOs to HTTP responses as HTML documents.
025 *
026 * <h5 class='topic'>Media types</h5>
027 *
028 * Handles <c>Accept</c> types:  <bc>text/html</bc>
029 * <p>
030 * Produces <c>Content-Type</c> types:  <bc>text/html</bc>
031 *
032 * <h5 class='topic'>Description</h5>
033 *
034 * Same as {@link HtmlSerializer}, except wraps the response in <code><xt>&lt;html&gt;</code>,
035 * <code><xt>&lt;head&gt;</code>, and <code><xt>&lt;body&gt;</code> tags so that it can be rendered in a browser.
036 *
037 * <p>
038 * Configurable properties are typically specified via <ja>@RestResource(properties)</ja> and <ja>@RestMethod(properties)</ja>
039 * annotations, although they can also be set programmatically via the <c>RestResponse.setProperty()</c> method.
040 *
041 * <h5 class='section'>Example:</h5>
042 * <p class='bcode w800'>
043 *    <ja>@RestResource</ja>(
044 *       messages=<js>"nls/AddressBookResource"</js>,
045 *       properties={
046 *          <ja>@Property</ja>(name=HtmlDocSerializer.<jsf>HTMLDOC_title</jsf>, value=<js>"$L{title}"</js>),
047 *          <ja>@Property</ja>(name=HtmlDocSerializer.<jsf>HTMLDOC_description</jsf>, value=<js>"$L{description}"</js>),
048 *          <ja>@Property</ja>(name=HtmlDocSerializer.<jsf>HTMLDOC_navlinks</jsf>, value=<js>"{options:'?method=OPTIONS',doc:'doc'}"</js>)
049 *       }
050 *    )
051 *    <jk>public class</jk> AddressBookResource <jk>extends</jk> BasicRestServletJena {
052 * </p>
053 *
054 * <p>
055 * Note that shortcut annotations are also provided for these particular settings:
056 * <p class='bcode w800'>
057 *    <ja>@RestResource</ja>(
058 *       messages=<js>"nls/AddressBookResource"</js>,
059 *       title=<js>"$L{title}"</js>,
060 *       description=<js>"$L{description}"</js>,
061 *       htmldoc=<ja>@HtmlDoc</ja>(
062 *          navlinks={
063 *             <js>"options: ?method=OPTIONS"</js>,
064 *             <js>"doc: doc"</js>
065 *          }
066 *       )
067 *    )
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@ConfigurableContext
077public class HtmlDocSerializer extends HtmlStrippedDocSerializer {
078
079   //-------------------------------------------------------------------------------------------------------------------
080   // Configurable properties
081   //-------------------------------------------------------------------------------------------------------------------
082
083   static final String PREFIX = "HtmlDocSerializer";
084
085   /**
086    * Configuration property:  Aside section contents.
087    *
088    * <h5 class='section'>Property:</h5>
089    * <ul>
090    *    <li><b>Name:</b>  <js>"HtmlDocSerializer.aside.ls"</js>
091    *    <li><b>Data type:</b>  <c>List&lt;String&gt;</c>
092    *    <li><b>Default:</b>  empty list
093    *    <li><b>Session property:</b>  <jk>true</jk>
094    * </ul>
095    *
096    * <h5 class='section'>Description:</h5>
097    * <p>
098    * Allows you to specify the contents of the aside section on the HTML page.
099    * The aside section floats on the right of the page for providing content supporting the serialized content of
100    * the page.
101    *
102    * <p>
103    * By default, the aside section is empty.
104    *
105    * <h5 class='section'>Example:</h5>
106    * <p class='bcode w800'>
107    *    <ja>@RestResource</ja>(
108    *       htmldoc=<ja>@HtmlDoc</ja>(
109    *          aside={
110    *             <js>"&lt;ul&gt;"</js>,
111    *             <js>" &lt;li&gt;Item 1"</js>,
112    *             <js>" &lt;li&gt;Item 2"</js>,
113    *             <js>" &lt;li&gt;Item 3"</js>,
114    *             <js>"&lt;/ul&gt;"</js>
115    *          }
116    *       )
117    *    )
118    * </p>
119    */
120   public static final String HTMLDOC_aside = PREFIX + ".aside.ls";
121
122   /**
123    * Configuration property:  Footer section contents.
124    *
125    * <h5 class='section'>Property:</h5>
126    * <ul>
127    *    <li><b>Name:</b>  <js>"HtmlDocSerializer.footer.ls"</js>
128    *    <li><b>Data type:</b>  <c>List&lt;String&gt;</c>
129    *    <li><b>Default:</b>  empty list
130    *    <li><b>Session property:</b>  <jk>true</jk>
131    * </ul>
132    *
133    * <h5 class='section'>Description:</h5>
134    * <p>
135    * Allows you to specify the contents of the footer section on the HTML page.
136    *
137    * <p>
138    * By default, the footer section is empty.
139    *
140    * <h5 class='section'>Example:</h5>
141    * <p class='bcode w800'>
142    *    <ja>@RestResource</ja>(
143    *       htmldoc=<ja>@HtmlDoc</ja>(
144    *          footer={
145    *             <js>"&lt;b&gt;This interface is great!&lt;/b&gt;"</js>
146    *          }
147    *       )
148    *    )
149    * </p>
150    */
151   public static final String HTMLDOC_footer = PREFIX + ".footer.ls";
152
153   /**
154    * Configuration property:  Additional head section content.
155    *
156    * <h5 class='section'>Property:</h5>
157    * <ul>
158    *    <li><b>Name:</b>  <js>"HtmlDocSerializer.head.ls"</js>
159    *    <li><b>Data type:</b>  <c>List&lt;String&gt;</c>
160    *    <li><b>Default:</b>  empty list
161    *    <li><b>Session property:</b>  <jk>true</jk>
162    * </ul>
163    *
164    * <h5 class='section'>Description:</h5>
165    * <p>
166    * Adds the specified HTML content to the head section of the page.
167    *
168    * <h5 class='section'>Example:</h5>
169    * <p class='bcode w800'>
170    *    <ja>@RestResource</ja>(
171    *       properties={
172    *          <ja>@Property</ja>(name=HtmlDocSerializer.<jsf>HTMLDOC_links</jsf>,
173    *             value=<js>"['&lt;link rel=\"icon\" href=\"htdocs/mypageicon.ico\"&gt;']"</js>)
174    *       }
175    *    )
176    * </p>
177    *
178    * <p>
179    * A shortcut on <ja>@RestResource</ja> is also provided for this setting:
180    * <p class='bcode w800'>
181    *    <ja>@RestResource</ja>(
182    *       htmldoc=@HtmlDoc(
183    *          head={
184    *             <js>"&lt;link rel='icon' href='$U{servlet:/htdocs/mypageicon.ico}'&gt;"</js>
185    *          }
186    *       )
187    *    )
188    * </p>
189    */
190   public static final String HTMLDOC_head = PREFIX + ".head.ls";
191
192   /**
193    * Configuration property:  Header section contents.
194    *
195    * <h5 class='section'>Property:</h5>
196    * <ul>
197    *    <li><b>Name:</b>  <js>"HtmlDocSerializer.header.ls"</js>
198    *    <li><b>Data type:</b>  <c>List&lt;String&gt;</c>
199    *    <li><b>Default:</b>  empty list
200    *    <li><b>Session property:</b>  <jk>true</jk>
201    * </ul>
202    *
203    * <h5 class='section'>Description:</h5>
204    * <p>
205    * Allows you to override the contents of the header section on the HTML page.
206    * The header section normally contains the title and description at the top of the page.
207    *
208    * <h5 class='section'>Example:</h5>
209    * <p class='bcode w800'>
210    *    <ja>@RestResource</ja>(
211    *       htmldoc=<ja>@HtmlDoc</ja>(
212    *          header={
213    *             <js>"&lt;h1&gt;My own header&lt;/h1&gt;"</js>
214    *          }
215    *       )
216    *    )
217    * </p>
218    */
219   public static final String HTMLDOC_header = PREFIX + ".header.ls";
220
221   /**
222    * Configuration property:  Nav section contents.
223    *
224    * <h5 class='section'>Property:</h5>
225    * <ul>
226    *    <li><b>Name:</b>  <js>"HtmlDocSerializer.nav.ls"</js>
227    *    <li><b>Data type:</b>  <c>List&lt;String&gt;</c>
228    *    <li><b>Default:</b>  empty list
229    *    <li><b>Session property:</b>  <jk>true</jk>
230    * </ul>
231    *
232    * <h5 class='section'>Description:</h5>
233    * <p>
234    * Allows you to override the contents of the nav section on the HTML page.
235    * The nav section normally contains the page links at the top of the page.
236    *
237    * <h5 class='section'>Example:</h5>
238    * <p class='bcode w800'>
239    *    <ja>@RestResource</ja>(
240    *       htmldoc=<ja>@HtmlDoc</ja>(
241    *          nav={
242    *             <js>"&lt;p class='special-navigation'&gt;This is my special navigation content&lt;/p&gt;"</js>
243    *          }
244    *       )
245    *    )
246    * </p>
247    *
248    * <p>
249    * When this property is specified, the {@link #HTMLDOC_navlinks} property is ignored.
250    */
251   public static final String HTMLDOC_nav = PREFIX + ".nav.ls";
252
253   /**
254    * Configuration property:  Page navigation links.
255    *
256    * <h5 class='section'>Property:</h5>
257    * <ul>
258    *    <li><b>Name:</b>  <js>"HtmlDocSerializer.navlinks.ls"</js>
259    *    <li><b>Data type:</b>  <c>List&lt;String&gt;</c>
260    *    <li><b>Default:</b>  empty list
261    *    <li><b>Session property:</b>  <jk>true</jk>
262    * </ul>
263    *
264    * <h5 class='section'>Description:</h5>
265    * <p>
266    * Adds a list of hyperlinks immediately under the title and description but above the content of the page.
267    *
268    * <p>
269    * This can be used to provide convenient hyperlinks when viewing the REST interface from a browser.
270    *
271    * <p>
272    * The value is an array of strings with two possible values:
273    * <ul>
274    *    <li>A key-value pair representing a hyperlink label and href:
275    *       <br><js>"google: http://google.com"</js>
276    *    <li>Arbitrary HTML.
277    * </ul>
278    *
279    * <p>
280    * Relative URLs are considered relative to the servlet path.
281    * For example, if the servlet path is <js>"http://localhost/myContext/myServlet"</js>, and the
282    * URL is <js>"foo"</js>, the link becomes <js>"http://localhost/myContext/myServlet/foo"</js>.
283    * Absolute (<js>"/myOtherContext/foo"</js>) and fully-qualified (<js>"http://localhost2/foo"</js>) URLs
284    * can also be used in addition to various other protocols specified by {@link UriResolver} such as
285    * <js>"servlet:/..."</js>.
286    *
287    * <h5 class='section'>Example:</h5>
288    * <p>
289    * The <c>AddressBookResource</c> sample class uses this property...
290    * <p class='bcode w800'>
291    *    <ja>@RestResource</ja>(
292    *       properties={
293    *          <ja>@Property</ja>(name=HtmlDocSerializer.<jsf>HTMLDOC_navlinks</jsf>,
294    *             value=<js>"['options: ?method=OPTIONS', 'doc: doc']"</js>)
295    *       }
296    *    )
297    *    <jk>public class</jk> AddressBookResource <jk>extends</jk> BasicRestServletJena {
298    * </p>
299    *
300    * <p>
301    * A shortcut on <ja>@RestResource</ja> is also provided for this setting:
302    * <p class='bcode w800'>
303    *    <ja>@RestResource</ja>(
304    *       htmldoc=@HtmlDoc(
305    *          navlinks={
306    *             <js>"options: ?method=OPTIONS"</js>,
307    *             <js>"doc: doc"</js>
308    *          }
309    *       )
310    *    )
311    *    <jk>public class</jk> AddressBookResource <jk>extends</jk> BasicRestServletJena {
312    * </p>
313    */
314   public static final String HTMLDOC_navlinks = PREFIX + ".navlinks.ls";
315
316   /**
317    * Configuration property:  Add to the {@link #HTMLDOC_navlinks} property.
318    */
319   public static final String HTMLDOC_navlinks_add = PREFIX + ".navlinks.ls/add";
320
321   /**
322    * Configuration property:  No-results message.
323    *
324    * <h5 class='section'>Property:</h5>
325    * <ul>
326    *    <li><b>Name:</b>  <js>"HtmlDocSerializer.noResultsMessage.s"</js>
327    *    <li><b>Data type:</b>  <c>String</c>
328    *    <li><b>Default:</b>  <js>"&lt;p&gt;no results&lt;/p&gt;"</js>
329    *    <li><b>Session property:</b>  <jk>false</jk>
330    * </ul>
331    *
332    * <h5 class='section'>Description:</h5>
333    * <p>
334    * Allows you to specify the string message used when trying to serialize an empty array or empty list.
335    *
336    * <h5 class='section'>Example:</h5>
337    * <p class='bcode w800'>
338    *    <ja>@RestResource</ja>(
339    *       htmldoc=<ja>@HtmlDoc</ja>(
340    *          noResultsMessage=<js>"&lt;b&gt;This interface is great!&lt;/b&gt;"</js>
341    *       )
342    *    )
343    * </p>
344    *
345    * <p>
346    * A value of <js>"NONE"</js> can be used to represent no value to differentiate it from an empty string.
347    */
348   public static final String HTMLDOC_noResultsMessage = PREFIX + ".noResultsMessage.s";
349
350   /**
351    * Configuration property:  Prevent word wrap on page.
352    *
353    * <h5 class='section'>Property:</h5>
354    * <ul>
355    *    <li><b>Name:</b>  <js>"HtmlDocSerializer.nowrap.b"</js>
356    *    <li><b>Data type:</b>  <c>Boolean</c>
357    *    <li><b>Default:</b>  <jk>false</jk>
358    *    <li><b>Session property:</b>  <jk>false</jk>
359    * </ul>
360    *
361    * <h5 class='section'>Description:</h5>
362    * <p>
363    * Adds <js>"* {white-space:nowrap}"</js> to the CSS instructions on the page to prevent word wrapping.
364    */
365   public static final String HTMLDOC_nowrap = PREFIX + ".nowrap.b";
366
367   /**
368    * Configuration property:  Javascript code.
369    *
370    * <h5 class='section'>Property:</h5>
371    * <ul>
372    *    <li><b>Name:</b>  <js>"HtmlDocSerializer.script.ls"</js>
373    *    <li><b>Data type:</b>  <c>List&lt;String&gt;</c>
374    *    <li><b>Default:</b>  empty list
375    *    <li><b>Session property:</b>  <jk>true</jk>
376    * </ul>
377    *
378    * <h5 class='section'>Description:</h5>
379    * <p>
380    * Adds the specified Javascript code to the HTML page.
381    *
382    * <h5 class='section'>Example:</h5>
383    * <p class='bcode w800'>
384    *    <ja>@RestResource</ja>(
385    *       properties={
386    *          <ja>@Property</ja>(name=HtmlDocSerializer.<jsf>HTMLDOC_script</jsf>,
387    *             value=<js>"alert('hello!');"</js>)
388    *       }
389    *    )
390    * </p>
391    *
392    * <p>
393    * A shortcut on <ja>@RestResource</ja> is also provided for this setting:
394    * <p class='bcode w800'>
395    *    <ja>@RestResource</ja>(
396    *       htmldoc=@HtmlDoc(
397    *          script={
398    *             <js>"alert('hello!');"</js>
399    *          }
400    *       )
401    *    )
402    * </p>
403    */
404   public static final String HTMLDOC_script = PREFIX + ".script.ls";
405
406   /**
407    * Configuration property:  Add to the {@link #HTMLDOC_script} property.
408    */
409   public static final String HTMLDOC_script_add = PREFIX + ".script.ls/add";
410
411   /**
412    * Configuration property:  CSS style code.
413    *
414    * <h5 class='section'>Property:</h5>
415    * <ul>
416    *    <li><b>Name:</b>  <js>"HtmlDocSerializer.style.ls"</js>
417    *    <li><b>Data type:</b>  <c>List&lt;String&gt;</c>
418    *    <li><b>Default:</b>  empty list
419    *    <li><b>Session property:</b>  <jk>true</jk>
420    * </ul>
421    *
422    * <h5 class='section'>Description:</h5>
423    * <p>
424    * Adds the specified CSS instructions to the HTML page.
425    *
426    * <h5 class='section'>Example:</h5>
427    * <p class='bcode w800'>
428    *    <ja>@RestResource</ja>(
429    *       properties={
430    *          <ja>@Property</ja>(name=HtmlDocSerializer.<jsf>HTMLDOC_style</jsf>,
431    *             value=<js>"h3 { color: red; }\nh5 { font-weight: bold; }"</js>)
432    *       }
433    *    )
434    * </p>
435    *
436    * <p>
437    * A shortcut on <ja>@RestResource</ja> is also provided for this setting:
438    * <p class='bcode w800'>
439    *    <ja>@RestResource</ja>(
440    *       htmldoc=@HtmlDoc(
441    *          style={
442    *             <js>"h3 { color: red; }"</js>,
443    *             <js>"h5 { font-weight: bold; }"</js>
444    *          }
445    *       )
446    *    )
447    * </p>
448    */
449   public static final String HTMLDOC_style = PREFIX + ".style.ls";
450
451   /**
452    * Configuration property:  Add to the {@link #HTMLDOC_style} property.
453    */
454   public static final String HTMLDOC_style_add = PREFIX + ".style.ls/add";
455
456   /**
457    * Configuration property:  Stylesheet import URLs.
458    *
459    * <h5 class='section'>Property:</h5>
460    * <ul>
461    *    <li><b>Name:</b>  <js>"HtmlDocSerializer.stylesheet.ls"</js>
462    *    <li><b>Data type:</b>  <c>List&lt;String&gt;</c>
463    *    <li><b>Default:</b>  empty list
464    *    <li><b>Session property:</b>  <jk>true</jk>
465    * </ul>
466    *
467    * <h5 class='section'>Description:</h5>
468    * <p>
469    * Adds a link to the specified stylesheet URL.
470    *
471    * <p>
472    * Note that this stylesheet is controlled by the <code><ja>@RestResource</ja>.stylesheet()</code> annotation.
473    */
474   public static final String HTMLDOC_stylesheet = PREFIX + ".stylesheet.ls";
475
476   /**
477    * Configuration property:  Add to the {@link #HTMLDOC_stylesheet} property.
478    */
479   public static final String HTMLDOC_stylesheet_add = PREFIX + ".stylesheet.ls/add";
480
481   /**
482    * Configuration property:  HTML document template.
483    *
484    * <h5 class='section'>Property:</h5>
485    * <ul>
486    *    <li><b>Name:</b>  <js>"HtmlDocSerializer.template.c"</js>
487    *    <li><b>Data type:</b>  <code>Class&lt;? <jk>extends</jk> HtmlDocTemplate&gt;</code>
488    *    <li><b>Default:</b>  <code>HtmlDocTemplateBasic.<jk>class</jk></code>
489    *    <li><b>Session property:</b>  <jk>false</jk>
490    * </ul>
491    *
492    * <h5 class='section'>Description:</h5>
493    * <p>
494    * Specifies the template to use for serializing the page.
495    *
496    * <p>
497    * By default, the {@link BasicHtmlDocTemplate} class is used to construct the contents of the HTML page, but
498    * can be overridden with your own custom implementation class.
499    *
500    * <h5 class='section'>Example:</h5>
501    * <p class='bcode w800'>
502    *    <ja>@RestResource</ja>(
503    *       htmldoc=@HtmlDoc(
504    *          template=MySpecialDocTemplate.<jk>class</jk>
505    *       )
506    *    )
507    * </p>
508    */
509   public static final String HTMLDOC_template = PREFIX + ".template.c";
510
511   /**
512    * Configuration property:  HTML Widgets.
513    *
514    * <h5 class='section'>Property:</h5>
515    * <ul>
516    *    <li><b>Name:</b>  <js>"HtmlDocSerializer.widgets.lo"</js>
517    *    <li><b>Data type:</b>  <code>List&lt;{@link HtmlWidget} | Class&lt;? <jk>extends</jk> {@link HtmlWidget}&gt;&gt;</code>
518    *    <li><b>Default:</b>  empty list
519    *    <li><b>Session property:</b>  <jk>false</jk>
520    *    <li><b>Annotations:</b>
521    *       <ul>
522    *          <li class='ja'>{@link HtmlDocConfig#widgets()}
523    *       </ul>
524    *    <li><b>Methods:</b>
525    *       <ul>
526    *          <li class='jm'>{@link HtmlDocSerializerBuilder#widgets(Class...)}
527    *          <li class='jm'>{@link HtmlDocSerializerBuilder#widgets(HtmlWidget...)}
528    *          <li class='jm'>{@link HtmlDocSerializerBuilder#widgetsReplace(Class...)}
529    *          <li class='jm'>{@link HtmlDocSerializerBuilder#widgetsReplace(HtmlWidget...)}
530    *       </ul>
531    * </ul>
532    *
533    * <h5 class='section'>Description:</h5>
534    * <p>
535    * Defines widgets that can be used in conjunction with string variables of the form <js>"$W{name}"</js>to quickly
536    * generate arbitrary replacement text.
537    *
538    * Widgets resolve the following variables:
539    * <ul class='spaced-list'>
540    *    <li><js>"$W{name}"</js> - Contents returned by {@link HtmlWidget#getHtml(VarResolverSession)}.
541    *    <li><js>"$W{name.script}"</js> - Contents returned by {@link HtmlWidget#getScript(VarResolverSession)}.
542    *       <br>The script contents are automatically inserted into the <xt>&lt;head/script&gt;</xt> section
543    *           in the HTML page.
544    *    <li><js>"$W{name.style}"</js> - Contents returned by {@link HtmlWidget#getStyle(VarResolverSession)}.
545    *       <br>The styles contents are automatically inserted into the <xt>&lt;head/style&gt;</xt> section
546    *           in the HTML page.
547    * </ul>
548    *
549    * <p>
550    * The following examples shows how to associate a widget with a REST method and then have it rendered in the links
551    * and aside section of the page:
552    *
553    * <p class='bcode w800'>
554    *    <ja>@HtmlDocSerializer</ja>(
555    *       widgets={
556    *          MyWidget.<jk>class</jk>
557    *       },
558    *       navlinks={
559    *          <js>"$W{MyWidget}"</js>
560    *       },
561    *       aside={
562    *          <js>"Check out this widget:  $W{MyWidget}"</js>
563    *       }
564    *    )
565    * </p>
566    *
567    * <ul class='notes'>
568    *    <li>
569    *       Widgets are inherited from super classes, but can be overridden by reusing the widget name.
570    * </ul>
571    *
572    * <ul class='seealso'>
573    *    <li class='link'>{@doc juneau-rest-server.HtmlDocAnnotation.Widgets}
574    * </ul>
575    */
576   public static final String HTMLDOC_widgets = PREFIX + ".widgets.lo";
577
578   //-------------------------------------------------------------------------------------------------------------------
579   // Predefined instances
580   //-------------------------------------------------------------------------------------------------------------------
581
582   /** Default serializer, all default settings. */
583   public static final HtmlDocSerializer DEFAULT = new HtmlDocSerializer(PropertyStore.DEFAULT);
584
585
586   //-------------------------------------------------------------------------------------------------------------------
587   // Instance
588   //-------------------------------------------------------------------------------------------------------------------
589
590   private final String[] style, stylesheet, script, navlinks, head, header, nav, aside, footer;
591   private final String noResultsMessage;
592   private final boolean nowrap;
593   private final HtmlDocTemplate template;
594   private final Map<String,HtmlWidget> widgets;
595
596   private volatile HtmlSchemaDocSerializer schemaSerializer;
597
598   /**
599    * Constructor.
600    *
601    * @param ps The property store containing all the settings for this object.
602    */
603   public HtmlDocSerializer(PropertyStore ps) {
604      this(ps, "text/html", (String)null);
605   }
606
607   /**
608    * Constructor.
609    *
610    * @param ps
611    *    The property store containing all the settings for this object.
612    * @param produces
613    *    The media type that this serializer produces.
614    * @param accept
615    *    The accept media types that the serializer can handle.
616    *    <p>
617    *    Can contain meta-characters per the <c>media-type</c> specification of
618    *    {@doc RFC2616.section14.1}
619    *    <p>
620    *    If empty, then assumes the only media type supported is <c>produces</c>.
621    *    <p>
622    *    For example, if this serializer produces <js>"application/json"</js> but should handle media types of
623    *    <js>"application/json"</js> and <js>"text/json"</js>, then the arguments should be:
624    *    <p class='bcode w800'>
625    *    <jk>super</jk>(ps, <js>"application/json"</js>, <js>"application/json",text/json"</js>);
626    *    </p>
627    *    <br>...or...
628    *    <p class='bcode w800'>
629    *    <jk>super</jk>(ps, <js>"application/json"</js>, <js>"*&#8203;/json"</js>);
630    *    </p>
631    * <p>
632    * The accept value can also contain q-values.
633    */
634   public HtmlDocSerializer(PropertyStore ps, String produces, String accept) {
635      super(ps, produces, accept);
636      style = getArrayProperty(HTMLDOC_style, String.class);
637      stylesheet = getArrayProperty(HTMLDOC_stylesheet, String.class);
638      script = getArrayProperty(HTMLDOC_script, String.class);
639      head = getArrayProperty(HTMLDOC_head, String.class);
640      header = getArrayProperty(HTMLDOC_header, String.class);
641      nav = getArrayProperty(HTMLDOC_nav, String.class);
642      aside = getArrayProperty(HTMLDOC_aside, String.class);
643      footer = getArrayProperty(HTMLDOC_footer, String.class);
644      nowrap = getBooleanProperty(HTMLDOC_nowrap, false);
645      navlinks = getArrayProperty(HTMLDOC_navlinks, String.class);
646      noResultsMessage = getStringProperty(HTMLDOC_noResultsMessage, "<p>no results</p>");
647      template = getInstanceProperty(HTMLDOC_template, HtmlDocTemplate.class, BasicHtmlDocTemplate.class);
648
649      Map<String,HtmlWidget> widgets = new HashMap<>();
650      for (HtmlWidget w : getInstanceArrayProperty(HTMLDOC_widgets, HtmlWidget.class, new HtmlWidget[0]))
651         widgets.put(w.getName(), w);
652      this.widgets = Collections.unmodifiableMap(widgets);
653   }
654
655   @Override /* Context */
656   public HtmlDocSerializerBuilder builder() {
657      return new HtmlDocSerializerBuilder(getPropertyStore());
658   }
659
660   /**
661    * Instantiates a new clean-slate {@link HtmlDocSerializerBuilder} object.
662    *
663    * <p>
664    * This is equivalent to simply calling <code><jk>new</jk> HtmlDocSerializerBuilder()</code>.
665    *
666    * <p>
667    * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies
668    * the settings of the object called on.
669    *
670    * @return A new {@link HtmlDocSerializerBuilder} object.
671    */
672   public static HtmlDocSerializerBuilder create() {
673      return new HtmlDocSerializerBuilder();
674   }
675
676   @Override /* Serializer */
677   public HtmlDocSerializerSession createSession() {
678      return createSession(createDefaultSessionArgs());
679   }
680
681   @Override /* Serializer */
682   public HtmlDocSerializerSession createSession(SerializerSessionArgs args) {
683      return new HtmlDocSerializerSession(this, args);
684   }
685
686   @Override /* XmlSerializer */
687   public HtmlSerializer getSchemaSerializer() {
688      if (schemaSerializer == null)
689         schemaSerializer = builder().build(HtmlSchemaDocSerializer.class);
690      return schemaSerializer;
691   }
692
693   //-----------------------------------------------------------------------------------------------------------------
694   // Properties
695   //-----------------------------------------------------------------------------------------------------------------
696
697   /**
698    * Configuration property:  Aside section contents.
699    *
700    * @see #HTMLDOC_aside
701    * @return
702    *    The overridden contents of the aside section on the HTML page.
703    */
704   protected final String[] getAside() {
705      return aside;
706   }
707
708   /**
709    * Configuration property:  Footer section contents.
710    *
711    * @see #HTMLDOC_footer
712    * @return
713    *    The overridden contents of the footer section on the HTML page.
714    */
715   protected final String[] getFooter() {
716      return footer;
717   }
718
719   /**
720    * Configuration property:  Additional head section content.
721    *
722    * @see #HTMLDOC_head
723    * @return
724    *    HTML content to add to the head section of the HTML page.
725    */
726   protected final String[] getHead() {
727      return head;
728   }
729
730   /**
731    * Configuration property:  Header section contents.
732    *
733    * @see #HTMLDOC_header
734    * @return
735    *    The overridden contents of the header section on the HTML page.
736    */
737   protected final String[] getHeader() {
738      return header;
739   }
740
741   /**
742    * Configuration property:  Nav section contents.
743    *
744    * @see #HTMLDOC_nav
745    * @return
746    *    The overridden contents of the nav section on the HTML page.
747    */
748   protected final String[] getNav() {
749      return nav;
750   }
751
752   /**
753    * Configuration property:  Page navigation links.
754    *
755    * @see #HTMLDOC_navlinks
756    * @return
757    *    Navigation links to add to the HTML page.
758    */
759   protected final String[] getNavlinks() {
760      return navlinks;
761   }
762
763   /**
764    * Configuration property:  No-results message.
765    *
766    * @see #HTMLDOC_noResultsMessage
767    * @return
768    *    The message used when serializing an empty array or empty list.
769    */
770   protected final String getNoResultsMessage() {
771      return noResultsMessage;
772   }
773
774   /**
775    * Configuration property:  Prevent word wrap on page.
776    *
777    * @see #HTMLDOC_nowrap
778    * @return
779    *    <jk>true</jk> if <js>"* {white-space:nowrap}"</js> shoudl be added to the CSS instructions on the page to prevent word wrapping.
780    */
781   protected final boolean isNowrap() {
782      return nowrap;
783   }
784
785   /**
786    * Configuration property:  Javascript code.
787    *
788    * @see #HTMLDOC_script
789    * @return
790    *    Arbitrary Javascript to add to the HTML page.
791    */
792   protected final String[] getScript() {
793      return script;
794   }
795
796   /**
797    * Configuration property:  CSS style code.
798    *
799    * @see #HTMLDOC_style
800    * @return
801    *    The CSS instructions to add to the HTML page.
802    */
803   protected final String[] getStyle() {
804      return style;
805   }
806
807   /**
808    * Configuration property:  Stylesheet import URLs.
809    *
810    * @see #HTMLDOC_stylesheet
811    * @return
812    *    The link to the stylesheet of the HTML page.
813    */
814   protected final String[] getStylesheet() {
815      return stylesheet;
816   }
817
818   /**
819    * Configuration property:  HTML document template.
820    *
821    * @see #HTMLDOC_template
822    * @return
823    *    The template to use for serializing the page.
824    */
825   protected final HtmlDocTemplate getTemplate() {
826      return template;
827   }
828
829   /**
830    * Configuration property:  HTML widgets.
831    *
832    * @see #HTMLDOC_widgets
833    * @return
834    *    Widgets defined on this serializers.
835    */
836   protected final Map<String,HtmlWidget> getWidgets() {
837      return widgets;
838   }
839
840   //-----------------------------------------------------------------------------------------------------------------
841   // Other methods
842   //-----------------------------------------------------------------------------------------------------------------
843
844   @Override /* Context */
845   public ObjectMap toMap() {
846      return super.toMap()
847         .append("HtmlDocSerializer", new DefaultFilteringObjectMap()
848            .append("header", header)
849            .append("nav", nav)
850            .append("navlinks", navlinks)
851            .append("aside", aside)
852            .append("footer", footer)
853            .append("style", style)
854            .append("head", head)
855            .append("stylesheet", stylesheet)
856            .append("nowrap", nowrap)
857            .append("template", template)
858            .append("noResultsMessage", noResultsMessage)
859            .append("widgets", widgets.keySet())
860         );
861   }
862}