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