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