001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.html;
018
019import static org.apache.juneau.collections.JsonMap.*;
020import static org.apache.juneau.common.utils.Utils.*;
021
022import java.lang.annotation.*;
023import java.nio.charset.*;
024import java.util.*;
025import java.util.concurrent.*;
026
027import org.apache.juneau.*;
028import org.apache.juneau.collections.*;
029import org.apache.juneau.html.annotation.*;
030import org.apache.juneau.internal.*;
031import org.apache.juneau.serializer.*;
032import org.apache.juneau.utils.*;
033import org.apache.juneau.xml.*;
034
035/**
036 * Serializes POJO models to HTML.
037 *
038 * <h5 class='topic'>Media types</h5>
039 * <p>
040 * Handles <c>Accept</c> types:  <bc>text/html</bc>
041 * <p>
042 * Produces <c>Content-Type</c> types:  <bc>text/html</bc>
043 *
044 * <h5 class='topic'>Description</h5>
045 * <p>
046 * The conversion is as follows...
047 * <ul class='spaced-list'>
048 *    <li>
049 *       {@link Map Maps} (e.g. {@link HashMap}, {@link TreeMap}) and beans are converted to HTML tables with
050 *       'key' and 'value' columns.
051 *    <li>
052 *       {@link Collection Collections} (e.g. {@link HashSet}, {@link LinkedList}) and Java arrays are converted
053 *       to HTML ordered lists.
054 *    <li>
055 *       {@code Collections} of {@code Maps} and beans are converted to HTML tables with keys as headers.
056 *    <li>
057 *       Everything else is converted to text.
058 * </ul>
059 *
060 * <p>
061 * This serializer provides several serialization options.  Typically, one of the predefined <jsf>DEFAULT</jsf>
062 * serializers will be sufficient.
063 * However, custom serializers can be constructed to fine-tune behavior.
064 *
065 * <p>
066 * The {@link HtmlLink} annotation can be used on beans to add hyperlinks to the output.
067 *
068 * <h5 class='topic'>Behavior-specific subclasses</h5>
069 * <p>
070 * The following direct subclasses are provided for convenience:
071 * <ul class='spaced-list'>
072 *    <li>
073 *       {@link Sq} - Default serializer, single quotes.
074 *    <li>
075 *       {@link SqReadable} - Default serializer, single quotes, whitespace added.
076 * </ul>
077 *
078 * <h5 class='section'>Example:</h5>
079 * <p class='bjava'>
080 *    <jc>// Use one of the default serializers to serialize a POJO</jc>
081 *    String <jv>html</jv> = HtmlSerializer.<jsf>DEFAULT</jsf>.serialize(<jv>someObject</jv>);
082 *
083 *    <jc>// Create a custom serializer that doesn't use whitespace and newlines</jc>
084 *    HtmlSerializer <jv>serializer</jv> = HtmlSerializer.<jsm>create</jsm>().ws().build();
085 *
086 *    <jc>// Same as above, except uses cloning</jc>
087 *    HtmlSerializer <jv>serializer</jv> = HtmlSerializer.<jsf>DEFAULT</jsf>.copy().ws().build();
088 *
089 *    <jc>// Serialize POJOs to HTML</jc>
090 *
091 *    <jc>// Produces: </jc>
092 *    <jc>// &lt;ul&gt;&lt;li&gt;1&lt;li&gt;2&lt;li&gt;3&lt;/ul&gt;</jc>
093 *    List <jv>list</jv> = JsonList.<jsm>of</jsm>(1, 2, 3);
094 *    String <jv>html</jv> = HtmlSerializer.<jsf>DEFAULT</jsf>.serialize(<jv>list</jv>);
095 *
096 *    <jc>// Produces: </jc>
097 *    <jc>//    &lt;table&gt; </jc>
098 *    <jc>//       &lt;tr&gt;&lt;th&gt;firstName&lt;/th&gt;&lt;th&gt;lastName&lt;/th&gt;&lt;/tr&gt; </jc>
099 *    <jc>//       &lt;tr&gt;&lt;td&gt;Bob&lt;/td&gt;&lt;td&gt;Costas&lt;/td&gt;&lt;/tr&gt; </jc>
100 *    <jc>//       &lt;tr&gt;&lt;td&gt;Billy&lt;/td&gt;&lt;td&gt;TheKid&lt;/td&gt;&lt;/tr&gt; </jc>
101 *    <jc>//       &lt;tr&gt;&lt;td&gt;Barney&lt;/td&gt;&lt;td&gt;Miller&lt;/td&gt;&lt;/tr&gt; </jc>
102 *    <jc>//    &lt;/table&gt; </jc>
103 *    <jv>html</jv> = JsonList.<jsm>of</jsm>(
104 *       JsonMap.<jsm>ofJson</jsm>(<js>"{firstName:'Bob',lastName:'Costas'}"</js>),
105 *       JsonMap.<jsm>ofJson</jsm>(<js>"{firstName:'Billy',lastName:'TheKid'}"</js>),
106 *       JsonMap.<jsm>ofJson</jsm>(<js>"{firstName:'Barney',lastName:'Miller'}"</js>)
107 *    );
108 *    String <jv>html</jv> = HtmlSerializer.<jsf>DEFAULT</jsf>.serialize(<jv>list</jv>);
109 *
110 *    <jc>// Produces: </jc>
111 *    <jc>//    &lt;table&gt; </jc>
112 *    <jc>//       &lt;tr&gt;&lt;th&gt;key&lt;/th&gt;&lt;th&gt;value&lt;/th&gt;&lt;/tr&gt; </jc>
113 *    <jc>//       &lt;tr&gt;&lt;td&gt;foo&lt;/td&gt;&lt;td&gt;bar&lt;/td&gt;&lt;/tr&gt; </jc>
114 *    <jc>//       &lt;tr&gt;&lt;td&gt;baz&lt;/td&gt;&lt;td&gt;123&lt;/td&gt;&lt;/tr&gt; </jc>
115 *    <jc>//    &lt;/table&gt; </jc>
116 *    Map <jv>map</jv> = JsonMap.<jsm>ofJson</jsm>(<js>"{foo:'bar',baz:123}"</js>);
117 *    String <jv>html</jv> = HtmlSerializer.<jsf>DEFAULT</jsf>.serialize(<jv>map</jv>);
118 *
119 *    <jc>// HTML elements can be nested arbitrarily deep</jc>
120 *    <jc>// Produces: </jc>
121 *    <jc>//   &lt;table&gt; </jc>
122 *    <jc>//      &lt;tr&gt;&lt;th&gt;key&lt;/th&gt;&lt;th&gt;value&lt;/th&gt;&lt;/tr&gt; </jc>
123 *    <jc>//      &lt;tr&gt;&lt;td&gt;foo&lt;/td&gt;&lt;td&gt;bar&lt;/td&gt;&lt;/tr&gt; </jc>
124 *    <jc>//      &lt;tr&gt;&lt;td&gt;baz&lt;/td&gt;&lt;td&gt;123&lt;/td&gt;&lt;/tr&gt; </jc>
125 *    <jc>//      &lt;tr&gt;&lt;td&gt;someNumbers&lt;/td&gt;&lt;td&gt;&lt;ul&gt;&lt;li&gt;1&lt;li&gt;2&lt;li&gt;3&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt; </jc>
126 *    <jc>//      &lt;tr&gt;&lt;td&gt;someSubMap&lt;/td&gt;&lt;td&gt; </jc>
127 *    <jc>//         &lt;table&gt; </jc>
128 *    <jc>//            &lt;tr&gt;&lt;th&gt;key&lt;/th&gt;&lt;th&gt;value&lt;/th&gt;&lt;/tr&gt; </jc>
129 *    <jc>//            &lt;tr&gt;&lt;td&gt;a&lt;/td&gt;&lt;td&gt;b&lt;/td&gt;&lt;/tr&gt; </jc>
130 *    <jc>//         &lt;/table&gt; </jc>
131 *    <jc>//      &lt;/td&gt;&lt;/tr&gt; </jc>
132 *    <jc>//   &lt;/table&gt; </jc>
133 *    Map <jv>map</jv> = JsonMap.<jsm>ofJson</jsm>(<js>"{foo:'bar',baz:123}"</js>);
134 *    <jv>map</jv>.put(<js>"someNumbers"</js>, JsonList.<jsm>of</jsm>(1, 2, 3));
135 *    <jv>map</jv>.put(<js>"someSubMap"</js>, JsonMap.<jsm>ofJson</jsm>(<js>"{a:'b'}"</js>));
136 *    String <jv>html</jv> = HtmlSerializer.<jsf>DEFAULT</jsf>.serialize(<jv>map</jv>);
137 * </p>
138 *
139 * <h5 class='section'>Notes:</h5><ul>
140 *    <li class='note'>This class is thread safe and reusable.
141 * </ul>
142 *
143 * <h5 class='section'>See Also:</h5><ul>
144 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HtmlBasics">HTML Basics</a>
145
146 * </ul>
147 */
148public class HtmlSerializer extends XmlSerializer implements HtmlMetaProvider {
149
150   //-------------------------------------------------------------------------------------------------------------------
151   // Static
152   //-------------------------------------------------------------------------------------------------------------------
153
154   /** Default serializer, all default settings. */
155   public static final HtmlSerializer DEFAULT = new HtmlSerializer(create());
156
157   /** Default serializer, single quotes. */
158   public static final HtmlSerializer DEFAULT_SQ = new HtmlSerializer.Sq(create());
159
160   /** Default serializer, single quotes, whitespace added. */
161   public static final HtmlSerializer DEFAULT_SQ_READABLE = new HtmlSerializer.SqReadable(create());
162
163   /** Default serializer, single quotes, simplified (no JSON type tags on strings). */
164   public static final HtmlSerializer DEFAULT_SIMPLE_SQ = new HtmlSerializer(create().sq().disableJsonTags());
165
166   /**
167    * Creates a new builder for this object.
168    *
169    * @return A new builder.
170    */
171   public static Builder create() {
172      return new Builder();
173   }
174
175   //-------------------------------------------------------------------------------------------------------------------
176   // Static subclasses
177   //-------------------------------------------------------------------------------------------------------------------
178
179   /** Default serializer, single quotes. */
180   public static class Sq extends HtmlSerializer {
181
182      /**
183       * Constructor.
184       *
185       * @param builder The builder for this object.
186       */
187      public Sq(Builder builder) {
188         super(builder.quoteChar('\''));
189      }
190   }
191
192   /** Default serializer, single quotes, whitespace added. */
193   public static class SqReadable extends HtmlSerializer {
194
195      /**
196       * Constructor.
197       *
198       * @param builder The builder for this object.
199       */
200      public SqReadable(Builder builder) {
201         super(builder.quoteChar('\'').useWhitespace());
202      }
203   }
204
205   //-------------------------------------------------------------------------------------------------------------------
206   // Instance
207   //-------------------------------------------------------------------------------------------------------------------
208
209   /**
210    * Builder class.
211    */
212   public static class Builder extends XmlSerializer.Builder {
213
214      private static final Cache<HashKey,HtmlSerializer> CACHE = Cache.of(HashKey.class, HtmlSerializer.class).build();
215
216      boolean addBeanTypesHtml, addKeyValueTableHeaders, disableDetectLabelParameters, disableDetectLinksInStrings;
217      String labelParameter;
218      AnchorText uriAnchorText;
219
220      /**
221       * Constructor, default settings.
222       */
223      protected Builder() {
224         produces("text/html");
225         addBeanTypesHtml = env("HtmlSerializer.addBeanTypesHtml", false);
226         addKeyValueTableHeaders = env("HtmlSerializer.addKeyValueTableHeaders", false);
227         disableDetectLabelParameters = env("HtmlSerializer.disableDetectLabelParameters", false);
228         disableDetectLinksInStrings = env("HtmlSerializer.disableDetectLinksInStrings", false);
229         uriAnchorText = env("HtmlSerializer.uriAnchorText", AnchorText.TO_STRING);
230         labelParameter =  env("HtmlSerializer.labelParameter", "label");
231      }
232
233      /**
234       * Copy constructor.
235       *
236       * @param copyFrom The bean to copy from.
237       */
238      protected Builder(HtmlSerializer copyFrom) {
239         super(copyFrom);
240         addBeanTypesHtml = copyFrom.addBeanTypesHtml;
241         addKeyValueTableHeaders = copyFrom.addKeyValueTableHeaders;
242         disableDetectLabelParameters = ! copyFrom.detectLabelParameters;
243         disableDetectLinksInStrings = ! copyFrom.detectLinksInStrings;
244         labelParameter = copyFrom.labelParameter;
245         uriAnchorText = copyFrom.uriAnchorText;
246      }
247
248      /**
249       * Copy constructor.
250       *
251       * @param copyFrom The builder to copy from.
252       */
253      protected Builder(Builder copyFrom) {
254         super(copyFrom);
255         addBeanTypesHtml = copyFrom.addBeanTypesHtml;
256         addKeyValueTableHeaders = copyFrom.addKeyValueTableHeaders;
257         disableDetectLabelParameters = copyFrom.disableDetectLabelParameters;
258         disableDetectLinksInStrings = copyFrom.disableDetectLinksInStrings;
259         labelParameter = copyFrom.labelParameter;
260         uriAnchorText = copyFrom.uriAnchorText;
261      }
262
263      @Override /* Context.Builder */
264      public Builder copy() {
265         return new Builder(this);
266      }
267
268      @Override /* Context.Builder */
269      public HtmlSerializer build() {
270         return cache(CACHE).build(HtmlSerializer.class);
271      }
272
273      @Override /* Context.Builder */
274      public HashKey hashKey() {
275         return HashKey.of(
276            super.hashKey(),
277            addBeanTypesHtml,
278            addKeyValueTableHeaders,
279            disableDetectLabelParameters,
280            disableDetectLinksInStrings,
281            labelParameter,
282            uriAnchorText
283         );
284      }
285
286      //-----------------------------------------------------------------------------------------------------------------
287      // Properties
288      //-----------------------------------------------------------------------------------------------------------------
289
290      /**
291       * Add <js>"_type"</js> properties when needed.
292       *
293       * <p>
294       * If <jk>true</jk>, then <js>"_type"</js> properties will be added to beans if their type cannot be inferred
295       * through reflection.
296       *
297       * <p>
298       * When present, this value overrides the {@link org.apache.juneau.serializer.Serializer.Builder#addBeanTypes()} setting and is
299       * provided to customize the behavior of specific serializers in a {@link SerializerSet}.
300       *
301       * @return This object.
302       */
303      public Builder addBeanTypesHtml() {
304         return addBeanTypesHtml(true);
305      }
306
307      /**
308       * Same as {@link #addBeanTypesHtml()} but allows you to explicitly specify the value.
309       *
310       * @param value The value for this setting.
311       * @return This object.
312       */
313      public Builder addBeanTypesHtml(boolean value) {
314         addBeanTypesHtml = value;
315         return this;
316      }
317
318      /**
319       * <i><l>HtmlSerializer</l> configuration property:&emsp;</i>  Add key/value headers on bean/map tables.
320       *
321       * <p>
322       * When enabled, <bc>key</bc> and <bc>value</bc> column headers are added to tables.
323       *
324       * <h5 class='section'>Example:</h5>
325       * <p class='bjson'>
326       *    <jc>// Our bean class.</jc>
327       *    <jk>public class</jk> MyBean {
328       *       <jk>public</jk> String <jf>f1</jf> = <js>"foo"</js>;
329       *       <jk>public</jk> String <jf>f2</jf> = <js>"bar"</js>;
330       *    }
331       *
332       *  <jc>// Serializer without headers.</jc>
333       *    WriterSerializer <jv>serializer1</jv> = HtmlSerializer.<jsf>DEFAULT</jsf>;
334       *
335       *  <jc>// Serializer with headers.</jc>
336       *    WriterSerializer <jv>serializer2</jv> = HtmlSerializer
337       *       .<jsm>create</jsm>()
338       *       .addKeyValueTableHeaders()
339       *       .build();
340       *
341       *    String <jv>withoutHeaders</jv> = <jv>serializer1</jv>.serialize(<jk>new</jk> MyBean());
342       *    String <jv>withHeaders</jv> = <jv>serializer2</jv>.serialize(<jk>new</jk> MyBean());
343       * </p>
344       *
345       * <p>
346       * The following shows the difference between the two generated outputs:
347       *
348       * <table class='styled'>
349       *    <tr>
350       *       <th><c>withoutHeaders</c></th>
351       *       <th><c>withHeaders</c></th>
352       *    </tr>
353       *    <tr>
354       *       <td>
355       *          <table class='unstyled'>
356       *             <tr><td>f1</td><td>foo</td></tr>
357       *             <tr><td>f2</td><td>bar</td></tr>
358       *          </table>
359       *       </td>
360       *       <td>
361       *          <table class='unstyled'>
362       *             <tr><th>key</th><th>value</th></tr>
363       *             <tr><td>f1</td><td>foo</td></tr>
364       *             <tr><td>f2</td><td>bar</td></tr>
365       *          </table>
366       *       </td>
367       *    </tr>
368       * </table>
369       *
370       * @return This object.
371       */
372      public Builder addKeyValueTableHeaders() {
373         return addKeyValueTableHeaders(true);
374      }
375
376      /**
377       * Same as {@link #addKeyValueTableHeaders()} but allows you to explicitly specify the value.
378       *
379       * @param value The value for this setting.
380       * @return This object.
381       */
382      public Builder addKeyValueTableHeaders(boolean value) {
383         addKeyValueTableHeaders = value;
384         return this;
385      }
386
387      /**
388       * <i><l>HtmlSerializer</l> configuration property:&emsp;</i>  Don't look for URLs in {@link String Strings}.
389       *
390       * <p>
391       * Disables the feature where if a string looks like a URL (i.e. starts with <js>"http://"</js> or <js>"https://"</js>, then treat it like a URL
392       * and make it into a hyperlink based on the rules specified by {@link Builder#uriAnchorText(AnchorText)}.
393       *
394       * <h5 class='section'>Example:</h5>
395       * <p class='bjson'>
396       *    <jc>// Our bean class with a property containing what looks like a URL.</jc>
397       *    <jk>public class</jk> MyBean {
398       *       <jk>public</jk> String <jf>f1</jf> = <js>"http://www.apache.org"</js>;
399       *    }
400       *
401       *  <jc>// Serializer with link detection.</jc>
402       *    WriterSerializer <jv>serializer1</jv> = HtmlSerializer
403       *       .<jsm>create</jsm>()
404       *       .addKeyValueTableHeaders()
405       *       .build();
406       *
407       *  <jc>// Serializer without link detection.</jc>
408       *    WriterSerializer <jv>serializer2</jv> = HtmlSerializer
409       *       .<jsm>create</jsm>()
410       *       .addKeyValueTableHeaders()
411       *       .disableDetectLinksInStrings()
412       *       .build();
413       *
414       *    String <jv>withLinks</jv> = <jv>serializer1</jv>.serialize(<jk>new</jk> MyBean());
415       *    String <jv>withoutLinks</jv> = <jv>serializer2</jv>.serialize(<jk>new</jk> MyBean());
416       * </p>
417       *
418       * <p>
419       * The following shows the difference between the two generated outputs:
420       *
421       * <table class='styled'>
422       *    <tr>
423       *       <th><c>withLinks</c></th>
424       *       <th><c>withoutLinks</c></th>
425       *    </tr>
426       *    <tr>
427       *       <td>
428       *          <table class='unstyled'>
429       *             <tr><th>key</th><th>value</th></tr>
430       *             <tr><td>f1</td><td><a href='http://www.apache.org'>http://www.apache.org</a></td></tr>
431       *          </table>
432       *       </td>
433       *       <td>
434       *          <table class='unstyled'>
435       *             <tr><th>key</th><th>value</th></tr>
436       *             <tr><td>f1</td><td>http://www.apache.org</td></tr>
437       *          </table>
438       *       </td>
439       *    </tr>
440       * </table>
441       *
442       * @return This object.
443       */
444      public Builder disableDetectLinksInStrings() {
445         return disableDetectLinksInStrings(true);
446      }
447
448      /**
449       * Same as {@link #disableDetectLinksInStrings()} but allows you to explicitly specify the value.
450       *
451       * @param value The value for this setting.
452       * @return This object.
453       */
454      public Builder disableDetectLinksInStrings(boolean value) {
455         disableDetectLinksInStrings = value;
456         return this;
457      }
458
459   @Override /* XmlSerializer.Builder */
460   public Builder disableJsonTags() {
461      super.disableJsonTags();
462      return this;
463   }
464
465   @Override /* XmlSerializer.Builder */
466   public Builder disableJsonTags(boolean value) {
467      super.disableJsonTags(value);
468      return this;
469   }
470
471      /**
472       * <i><l>HtmlSerializer</l> configuration property:&emsp;</i>  Link label parameter name.
473       *
474       * <p>
475       * The parameter name to look for when resolving link labels}.
476       *
477       * @param value
478       *    The new value for this property.
479       *    <br>The default is <js>"label"</js>.
480       * @return This object.
481       */
482      public Builder labelParameter(String value) {
483         labelParameter = value;
484         return this;
485      }
486
487      /**
488       * <i><l>HtmlSerializer</l> configuration property:&emsp;</i>  Dont look for link labels in URIs.
489       *
490       * <p>
491       * Disables the feature where if the URL has a label parameter (e.g. <js>"?label=foobar"</js>), then use that as the anchor text of the link.
492       *
493       * <p>
494       * The parameter name can be changed via the {@link #labelParameter(String)} property.
495       *
496       * <h5 class='section'>Example:</h5>
497       * <p class='bjson'>
498       *    <jc>// Our bean class with a property containing what looks like a URL.</jc>
499       *    <jk>public class</jk> MyBean {
500       *       <jk>public</jk> URI <jf>f1</jf> = URI.<jsm>create</jsm>(<js>"http://www.apache.org?label=Apache%20Foundation"</js>);
501       *    }
502       *
503       *  <jc>// Serializer with label detection.</jc>
504       *    WriterSerializer <jv>serializer1</jv> = HtmlSerializer
505       *       .<jsm>create</jsm>()
506       *       .addKeyValueTableHeaders()
507       *       .build();
508       *
509       *  <jc>// Serializer without label detection.</jc>
510       *    WriterSerializer <jv>serializer2</jv> = HtmlSerializer
511       *       .<jsm>create</jsm>()
512       *       .addKeyValueTableHeaders()
513       *       .disableDetectLabelParameters()
514       *       .build();
515       *
516       *    String <jv>withLabels</jv> = <jv>serializer1</jv>.serialize(<jk>new</jk> MyBean());
517       *    String <jv>withoutLabels</jv> = <jv>serializer2</jv>.serialize(<jk>new</jk> MyBean());
518       * </p>
519       *
520       * <p>
521       * The following shows the difference between the two generated outputs.
522       * <br>Note that they're both hyperlinks, but the anchor text differs:
523       *
524       * <table class='styled'>
525       *    <tr>
526       *       <th><c>withLabels</c></th>
527       *       <th><c>withoutLabels</c></th>
528       *    </tr>
529       *    <tr>
530       *       <td>
531       *          <table class='unstyled'>
532       *             <tr><th>key</th><th>value</th></tr>
533       *             <tr><td>f1</td><td><a href='http://www.apache.org?label=Apache%20Foundation'>Apache Foundation</a></td></tr>
534       *          </table>
535       *       </td>
536       *       <td>
537       *          <table class='unstyled'>
538       *             <tr><th>key</th><th>value</th></tr>
539       *             <tr><td>f1</td><td><a href='http://www.apache.org?label=Apache%20Foundation'>http://www.apache.org?label=Apache%20Foundation</a></td></tr>
540       *          </table>
541       *       </td>
542       *    </tr>
543       * </table>
544       *
545       * @return This object.
546       */
547      public Builder disableDetectLabelParameters() {
548         return disableDetectLabelParameters(true);
549      }
550
551      /**
552       * Same as {@link #disableDetectLabelParameters()} but allows you to explicitly specify the value.
553       *
554       * @param value The value for this setting.
555       * @return This object.
556       */
557      public Builder disableDetectLabelParameters(boolean value) {
558         disableDetectLabelParameters = value;
559         return this;
560      }
561
562      /**
563       * <i><l>HtmlSerializer</l> configuration property:&emsp;</i>  Anchor text source.
564       *
565       * <p>
566       * When creating anchor tags (e.g. <code><xt>&lt;a</xt> <xa>href</xa>=<xs>'...'</xs>
567       * <xt>&gt;</xt>text<xt>&lt;/a&gt;</xt></code>) in HTML, this setting defines what to set the inner text to.
568       *
569       * <p>
570       * The possible values are:
571       * <ul>
572       *    <li class='jc'>{@link AnchorText}
573       *    <ul>
574       *       <li class='jf'>{@link AnchorText#TO_STRING TO_STRING} (default) - Set to whatever is returned by {@link #toString()} on the object.
575       *          <br>
576       *          <h5 class='section'>Example:</h5>
577       *          <p class='bjson'>
578       *    <jc>// Our bean class with a URI property.</jc>
579       *    <jk>public class</jk> MyBean {
580       *       <jk>public</jk> URI <jf>f1</jf> = URI.<jsm>create</jsm>(<js>"http://www.apache.org?foo=bar#myAnchor"</js>);
581       *    }
582       *
583       *    <jc>// Serializer with TO_STRING anchor text.</jc>
584       *    WriterSerializer <jv>serializer1</jv> = HtmlSerializer.<jsm>create</jsm>().anchorText(<jsf>TO_STRING</jsf>).build();
585       *
586       *    <jc>// Produces: &lt;a href='http://www.apache.org?foo=bar#myAnchor'&gt;http://www.apache.org?foo=bar#myAnchor&lt;/a&gt;</jc>
587       *    String <jv>html</jv> = <jv>serializer1</jv>.serialize(<jk>new</jk> MyBean());
588       *          </p>
589       *       <li class='jf'>{@link AnchorText#PROPERTY_NAME PROPERTY_NAME} - Set to the bean property name.
590       *          <br>
591       *          <h5 class='section'>Example:</h5>
592       *          <p class='bjson'>
593       *    <jc>// Our bean class with a URI property.</jc>
594       *    <jk>public class</jk> MyBean {
595       *       <jk>public</jk> URI <jf>f1</jf> = URI.<jsm>create</jsm>(<js>"http://www.apache.org?foo=bar#myAnchor"</js>);
596       *    }
597       *
598       *    <jc>// Serializer with PROPERTY_NAME anchor text.</jc>
599       *    WriterSerializer <jv>serializer1</jv> = HtmlSerializer.<jsm>create</jsm>().anchorText(<jsf>PROPERTY_NAME</jsf>).build();
600       *
601       *    <jc>// Produces: &lt;a href='http://www.apache.org?foo=bar#myAnchor'&gt;f1&lt;/a&gt;</jc>
602       *    String <jv>html</jv> = <jv>serializer1</jv>.serialize(<jk>new</jk> MyBean());
603       *          </p>
604       *       <li class='jf'>{@link AnchorText#URI URI} - Set to the URI value.
605       *          <br>
606       *          <h5 class='section'>Example:</h5>
607       *          <p class='bjson'>
608       *    <jc>// Our bean class with a URI property.</jc>
609       *    <jk>public class</jk> MyBean {
610       *       <jk>public</jk> URI <jf>f1</jf> = URI.<jsm>create</jsm>(<js>"http://www.apache.org?foo=bar#myAnchor"</js>);
611       *    }
612       *
613       *    <jc>// Serializer with URI anchor text.</jc>
614       *    WriterSerializer <jv>serializer1</jv> = HtmlSerializer.<jsm>create</jsm>().anchorText(<jsf>URI</jsf>).build();
615       *
616       *    <jc>// Produces: &lt;a href='http://www.apache.org?foo=bar#myAnchor'&gt;http://www.apache.org?foo=bar&lt;/a&gt;</jc>
617       *    String <jv>html</jv> = <jv>serializer1</jv>.serialize(<jk>new</jk> MyBean());
618       *          </p>
619       *       <li class='jf'>{@link AnchorText#LAST_TOKEN LAST_TOKEN} - Set to the last token of the URI value.
620       *          <br>
621       *          <h5 class='section'>Example:</h5>
622       *          <p class='bjson'>
623       *    <jc>// Our bean class with a URI property.</jc>
624       *    <jk>public class</jk> MyBean {
625       *       <jk>public</jk> URI <jf>f1</jf> = URI.<jsm>create</jsm>(<js>"http://www.apache.org/foo/bar?baz=qux#myAnchor"</js>);
626       *    }
627       *
628       *    <jc>// Serializer with LAST_TOKEN anchor text.</jc>
629       *    WriterSerializer <jv>serializer1</jv> = HtmlSerializer.<jsm>create</jsm>().anchorText(<jsf>LAST_TOKEN</jsf>).build();
630       *
631       *    <jc>// Produces: &lt;a href='http://www.apache.org/foo/bar?baz=qux#myAnchor'&gt;bar&lt;/a&gt;</jc>
632       *    String <jv>html</jv> = <jv>serializer1</jv>.serialize(<jk>new</jk> MyBean());
633       *          </p>
634       *       <li class='jf'>{@link AnchorText#URI_ANCHOR URI_ANCHOR} - Set to the anchor of the URL.
635       *          <br>
636       *          <h5 class='section'>Example:</h5>
637       *          <p class='bjson'>
638       *    <jc>// Our bean class with a URI property.</jc>
639       *    <jk>public class</jk> MyBean {
640       *       <jk>public</jk> URI <jf>f1</jf> = URI.<jsm>create</jsm>(<js>"http://www.apache.org/foo/bar?baz=qux#myAnchor"</js>);
641       *    }
642       *
643       *    <jc>// Serializer with URI_ANCHOR anchor text.</jc>
644       *    WriterSerializer <jv>serializer1</jv> = HtmlSerializer.<jsm>create</jsm>().anchorText(<jsf>URI_ANCHOR</jsf>).build();
645       *
646       *    <jc>// Produces: &lt;a href='http://www.apache.org/foo/bar?baz=qux#myAnchor'&gt;myAnchor&lt;/a&gt;</jc>
647       *    String <jv>html</jv> = <jv>serializer1</jv>.serialize(<jk>new</jk> MyBean());
648       *          </p>
649       *       <li class='jf'>{@link AnchorText#CONTEXT_RELATIVE CONTEXT_RELATIVE} - Same as {@link AnchorText#TO_STRING TO_STRING} but assumes it's a context-relative path.
650       *          <br>
651       *          <h5 class='section'>Example:</h5>
652       *          <p class='bjson'>
653       *    <jc>// Our bean class with a URI property.</jc>
654       *    <jk>public class</jk> MyBean {
655       *       <jk>public</jk> URI <jf>f1</jf> = URI.<jsm>create</jsm>(<js>"bar/baz"</js>);
656       *    }
657       *
658       *    <jc>// Serializer with CONTEXT_RELATIVE anchor text.</jc>
659       *    WriterSerializer <jv>serializer1</jv> = HtmlSerializer
660       *       .<jsm>create</jsm>()
661       *       .anchorText(<jsf>CONTEXT_RELATIVE</jsf>)
662       *       .uriResolution(<jsf>ROOT_RELATIVE</jsf>)
663       *       .uriRelativity(<jsf>RESOURCE</jsf>)
664       *       .uriContext(<js>"{authority:'http://localhost:10000',contextRoot:'/myContext',servletPath:'/myServlet',pathInfo:'/foo'}"</js>)
665       *       .build();
666       *
667       *    <jc>// Produces: &lt;a href&#61;'/myContext/myServlet/bar/baz'&gt;myServlet/bar/baz&lt;/a&gt;</jc>
668       *    String <jv>html</jv> = <jv>serializer1</jv>.serialize(<jk>new</jk> MyBean());
669       *          </p>
670       *       <li class='jf'>{@link AnchorText#SERVLET_RELATIVE SERVLET_RELATIVE} - Same as {@link AnchorText#TO_STRING TO_STRING} but assumes it's a servlet-relative path.
671       *          <br>
672       *          <h5 class='section'>Example:</h5>
673       *          <p class='bjson'>
674       *    <jc>// Our bean class with a URI property.</jc>
675       *    <jk>public class</jk> MyBean {
676       *       <jk>public</jk> URI <jf>f1</jf> = URI.<jsm>create</jsm>(<js>"bar/baz"</js>);
677       *    }
678       *
679       *    <jc>// Serializer with SERVLET_RELATIVE anchor text.</jc>
680       *    WriterSerializer <jv>serializer1</jv> = HtmlSerializer
681       *       .<jsm>create</jsm>()
682       *       .anchorText(<jsf>SERVLET_RELATIVE</jsf>)
683       *       .uriResolution(<jsf>ROOT_RELATIVE</jsf>)
684       *       .uriRelativity(<jsf>RESOURCE</jsf>)
685       *       .uriContext(<js>"{authority:'http://localhost:10000',contextRoot:'/myContext',servletPath:'/myServlet',pathInfo:'/foo'}"</js>)
686       *       .build();
687       *
688       *    <jc>// Produces: &lt;a href&#61;'/myContext/myServlet/bar/baz'&gt;bar/baz&lt;/a&gt;</jc>
689       *    String <jv>html</jv> = <jv>serializer1</jv>.serialize(<jk>new</jk> MyBean());
690       *          </p>
691       *       <li class='jf'>{@link AnchorText#PATH_RELATIVE PATH_RELATIVE} - Same as {@link AnchorText#TO_STRING TO_STRING} but assumes it's a path-relative path.
692       *          <br>
693       *          <h5 class='section'>Example:</h5>
694       *          <p class='bjson'>
695       *    <jc>// Our bean class with a URI property.</jc>
696       *    <jk>public class</jk> MyBean {
697       *       <jk>public</jk> URI <jf>f1</jf> = URI.<jsm>create</jsm>(<js>"bar/baz"</js>);
698       *    }
699       *
700       *    <jc>// Serializer with PATH_RELATIVE anchor text.</jc>
701       *    WriterSerializer <jv>serializer1</jv> = HtmlSerializer
702       *       .<jsm>create</jsm>()
703       *       .anchorText(<jsf>PATH_RELATIVE</jsf>)
704       *       .uriResolution(<jsf>ROOT_RELATIVE</jsf>)
705       *       .uriRelativity(<jsf>PATH_INFO</jsf>)
706       *       .uriContext(<js>"{authority:'http://localhost:10000',contextRoot:'/myContext',servletPath:'/myServlet',pathInfo:'/foo'}"</js>)
707       *       .build();
708       *
709       *    <jc>// Produces: &lt;a href&#61;'/myContext/myServlet/foo/bar/baz'&gt;bar/baz&lt;/a&gt;</jc>
710       *    String <jv>html</jv> = <jv>serializer1</jv>.serialize(<jk>new</jk> MyBean());
711       *          </p>
712       *    </ul>
713       * </ul>
714       *
715       * @param value
716       *    The new value for this property.
717       *    <br>The default is {@link AnchorText#TO_STRING}.
718       * @return This object.
719       */
720      public Builder uriAnchorText(AnchorText value) {
721         uriAnchorText = value;
722         return this;
723      }
724      @Override /* Overridden from Builder */
725      public Builder annotations(Annotation...values) {
726         super.annotations(values);
727         return this;
728      }
729
730      @Override /* Overridden from Builder */
731      public Builder apply(AnnotationWorkList work) {
732         super.apply(work);
733         return this;
734      }
735
736      @Override /* Overridden from Builder */
737      public Builder applyAnnotations(Object...from) {
738         super.applyAnnotations(from);
739         return this;
740      }
741
742      @Override /* Overridden from Builder */
743      public Builder applyAnnotations(Class<?>...from) {
744         super.applyAnnotations(from);
745         return this;
746      }
747
748      @Override /* Overridden from Builder */
749      public Builder cache(Cache<HashKey,? extends org.apache.juneau.Context> value) {
750         super.cache(value);
751         return this;
752      }
753
754      @Override /* Overridden from Builder */
755      public Builder debug() {
756         super.debug();
757         return this;
758      }
759
760      @Override /* Overridden from Builder */
761      public Builder debug(boolean value) {
762         super.debug(value);
763         return this;
764      }
765
766      @Override /* Overridden from Builder */
767      public Builder impl(Context value) {
768         super.impl(value);
769         return this;
770      }
771
772      @Override /* Overridden from Builder */
773      public Builder type(Class<? extends org.apache.juneau.Context> value) {
774         super.type(value);
775         return this;
776      }
777
778      @Override /* Overridden from Builder */
779      public Builder beanClassVisibility(Visibility value) {
780         super.beanClassVisibility(value);
781         return this;
782      }
783
784      @Override /* Overridden from Builder */
785      public Builder beanConstructorVisibility(Visibility value) {
786         super.beanConstructorVisibility(value);
787         return this;
788      }
789
790      @Override /* Overridden from Builder */
791      public Builder beanContext(BeanContext value) {
792         super.beanContext(value);
793         return this;
794      }
795
796      @Override /* Overridden from Builder */
797      public Builder beanContext(BeanContext.Builder value) {
798         super.beanContext(value);
799         return this;
800      }
801
802      @Override /* Overridden from Builder */
803      public Builder beanDictionary(java.lang.Class<?>...values) {
804         super.beanDictionary(values);
805         return this;
806      }
807
808      @Override /* Overridden from Builder */
809      public Builder beanFieldVisibility(Visibility value) {
810         super.beanFieldVisibility(value);
811         return this;
812      }
813
814      @Override /* Overridden from Builder */
815      public Builder beanInterceptor(Class<?> on, Class<? extends org.apache.juneau.swap.BeanInterceptor<?>> value) {
816         super.beanInterceptor(on, value);
817         return this;
818      }
819
820      @Override /* Overridden from Builder */
821      public Builder beanMapPutReturnsOldValue() {
822         super.beanMapPutReturnsOldValue();
823         return this;
824      }
825
826      @Override /* Overridden from Builder */
827      public Builder beanMethodVisibility(Visibility value) {
828         super.beanMethodVisibility(value);
829         return this;
830      }
831
832      @Override /* Overridden from Builder */
833      public Builder beanProperties(Map<String,Object> values) {
834         super.beanProperties(values);
835         return this;
836      }
837
838      @Override /* Overridden from Builder */
839      public Builder beanProperties(Class<?> beanClass, String properties) {
840         super.beanProperties(beanClass, properties);
841         return this;
842      }
843
844      @Override /* Overridden from Builder */
845      public Builder beanProperties(String beanClassName, String properties) {
846         super.beanProperties(beanClassName, properties);
847         return this;
848      }
849
850      @Override /* Overridden from Builder */
851      public Builder beanPropertiesExcludes(Map<String,Object> values) {
852         super.beanPropertiesExcludes(values);
853         return this;
854      }
855
856      @Override /* Overridden from Builder */
857      public Builder beanPropertiesExcludes(Class<?> beanClass, String properties) {
858         super.beanPropertiesExcludes(beanClass, properties);
859         return this;
860      }
861
862      @Override /* Overridden from Builder */
863      public Builder beanPropertiesExcludes(String beanClassName, String properties) {
864         super.beanPropertiesExcludes(beanClassName, properties);
865         return this;
866      }
867
868      @Override /* Overridden from Builder */
869      public Builder beanPropertiesReadOnly(Map<String,Object> values) {
870         super.beanPropertiesReadOnly(values);
871         return this;
872      }
873
874      @Override /* Overridden from Builder */
875      public Builder beanPropertiesReadOnly(Class<?> beanClass, String properties) {
876         super.beanPropertiesReadOnly(beanClass, properties);
877         return this;
878      }
879
880      @Override /* Overridden from Builder */
881      public Builder beanPropertiesReadOnly(String beanClassName, String properties) {
882         super.beanPropertiesReadOnly(beanClassName, properties);
883         return this;
884      }
885
886      @Override /* Overridden from Builder */
887      public Builder beanPropertiesWriteOnly(Map<String,Object> values) {
888         super.beanPropertiesWriteOnly(values);
889         return this;
890      }
891
892      @Override /* Overridden from Builder */
893      public Builder beanPropertiesWriteOnly(Class<?> beanClass, String properties) {
894         super.beanPropertiesWriteOnly(beanClass, properties);
895         return this;
896      }
897
898      @Override /* Overridden from Builder */
899      public Builder beanPropertiesWriteOnly(String beanClassName, String properties) {
900         super.beanPropertiesWriteOnly(beanClassName, properties);
901         return this;
902      }
903
904      @Override /* Overridden from Builder */
905      public Builder beansRequireDefaultConstructor() {
906         super.beansRequireDefaultConstructor();
907         return this;
908      }
909
910      @Override /* Overridden from Builder */
911      public Builder beansRequireSerializable() {
912         super.beansRequireSerializable();
913         return this;
914      }
915
916      @Override /* Overridden from Builder */
917      public Builder beansRequireSettersForGetters() {
918         super.beansRequireSettersForGetters();
919         return this;
920      }
921
922      @Override /* Overridden from Builder */
923      public Builder dictionaryOn(Class<?> on, java.lang.Class<?>...values) {
924         super.dictionaryOn(on, values);
925         return this;
926      }
927
928      @Override /* Overridden from Builder */
929      public Builder disableBeansRequireSomeProperties() {
930         super.disableBeansRequireSomeProperties();
931         return this;
932      }
933
934      @Override /* Overridden from Builder */
935      public Builder disableIgnoreMissingSetters() {
936         super.disableIgnoreMissingSetters();
937         return this;
938      }
939
940      @Override /* Overridden from Builder */
941      public Builder disableIgnoreTransientFields() {
942         super.disableIgnoreTransientFields();
943         return this;
944      }
945
946      @Override /* Overridden from Builder */
947      public Builder disableIgnoreUnknownNullBeanProperties() {
948         super.disableIgnoreUnknownNullBeanProperties();
949         return this;
950      }
951
952      @Override /* Overridden from Builder */
953      public Builder disableInterfaceProxies() {
954         super.disableInterfaceProxies();
955         return this;
956      }
957
958      @Override /* Overridden from Builder */
959      public <T> Builder example(Class<T> pojoClass, T o) {
960         super.example(pojoClass, o);
961         return this;
962      }
963
964      @Override /* Overridden from Builder */
965      public <T> Builder example(Class<T> pojoClass, String json) {
966         super.example(pojoClass, json);
967         return this;
968      }
969
970      @Override /* Overridden from Builder */
971      public Builder findFluentSetters() {
972         super.findFluentSetters();
973         return this;
974      }
975
976      @Override /* Overridden from Builder */
977      public Builder findFluentSetters(Class<?> on) {
978         super.findFluentSetters(on);
979         return this;
980      }
981
982      @Override /* Overridden from Builder */
983      public Builder ignoreInvocationExceptionsOnGetters() {
984         super.ignoreInvocationExceptionsOnGetters();
985         return this;
986      }
987
988      @Override /* Overridden from Builder */
989      public Builder ignoreInvocationExceptionsOnSetters() {
990         super.ignoreInvocationExceptionsOnSetters();
991         return this;
992      }
993
994      @Override /* Overridden from Builder */
995      public Builder ignoreUnknownBeanProperties() {
996         super.ignoreUnknownBeanProperties();
997         return this;
998      }
999
1000      @Override /* Overridden from Builder */
1001      public Builder ignoreUnknownEnumValues() {
1002         super.ignoreUnknownEnumValues();
1003         return this;
1004      }
1005
1006      @Override /* Overridden from Builder */
1007      public Builder implClass(Class<?> interfaceClass, Class<?> implClass) {
1008         super.implClass(interfaceClass, implClass);
1009         return this;
1010      }
1011
1012      @Override /* Overridden from Builder */
1013      public Builder implClasses(Map<Class<?>,Class<?>> values) {
1014         super.implClasses(values);
1015         return this;
1016      }
1017
1018      @Override /* Overridden from Builder */
1019      public Builder interfaceClass(Class<?> on, Class<?> value) {
1020         super.interfaceClass(on, value);
1021         return this;
1022      }
1023
1024      @Override /* Overridden from Builder */
1025      public Builder interfaces(java.lang.Class<?>...value) {
1026         super.interfaces(value);
1027         return this;
1028      }
1029
1030      @Override /* Overridden from Builder */
1031      public Builder locale(Locale value) {
1032         super.locale(value);
1033         return this;
1034      }
1035
1036      @Override /* Overridden from Builder */
1037      public Builder mediaType(MediaType value) {
1038         super.mediaType(value);
1039         return this;
1040      }
1041
1042      @Override /* Overridden from Builder */
1043      public Builder notBeanClasses(java.lang.Class<?>...values) {
1044         super.notBeanClasses(values);
1045         return this;
1046      }
1047
1048      @Override /* Overridden from Builder */
1049      public Builder notBeanPackages(String...values) {
1050         super.notBeanPackages(values);
1051         return this;
1052      }
1053
1054      @Override /* Overridden from Builder */
1055      public Builder propertyNamer(Class<? extends org.apache.juneau.PropertyNamer> value) {
1056         super.propertyNamer(value);
1057         return this;
1058      }
1059
1060      @Override /* Overridden from Builder */
1061      public Builder propertyNamer(Class<?> on, Class<? extends org.apache.juneau.PropertyNamer> value) {
1062         super.propertyNamer(on, value);
1063         return this;
1064      }
1065
1066      @Override /* Overridden from Builder */
1067      public Builder sortProperties() {
1068         super.sortProperties();
1069         return this;
1070      }
1071
1072      @Override /* Overridden from Builder */
1073      public Builder sortProperties(java.lang.Class<?>...on) {
1074         super.sortProperties(on);
1075         return this;
1076      }
1077
1078      @Override /* Overridden from Builder */
1079      public Builder stopClass(Class<?> on, Class<?> value) {
1080         super.stopClass(on, value);
1081         return this;
1082      }
1083
1084      @Override /* Overridden from Builder */
1085      public <T, S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction) {
1086         super.swap(normalClass, swappedClass, swapFunction);
1087         return this;
1088      }
1089
1090      @Override /* Overridden from Builder */
1091      public <T, S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction, ThrowingFunction<S,T> unswapFunction) {
1092         super.swap(normalClass, swappedClass, swapFunction, unswapFunction);
1093         return this;
1094      }
1095
1096      @Override /* Overridden from Builder */
1097      public Builder swaps(Object...values) {
1098         super.swaps(values);
1099         return this;
1100      }
1101
1102      @Override /* Overridden from Builder */
1103      public Builder swaps(Class<?>...values) {
1104         super.swaps(values);
1105         return this;
1106      }
1107
1108      @Override /* Overridden from Builder */
1109      public Builder timeZone(TimeZone value) {
1110         super.timeZone(value);
1111         return this;
1112      }
1113
1114      @Override /* Overridden from Builder */
1115      public Builder typeName(Class<?> on, String value) {
1116         super.typeName(on, value);
1117         return this;
1118      }
1119
1120      @Override /* Overridden from Builder */
1121      public Builder typePropertyName(String value) {
1122         super.typePropertyName(value);
1123         return this;
1124      }
1125
1126      @Override /* Overridden from Builder */
1127      public Builder typePropertyName(Class<?> on, String value) {
1128         super.typePropertyName(on, value);
1129         return this;
1130      }
1131
1132      @Override /* Overridden from Builder */
1133      public Builder useEnumNames() {
1134         super.useEnumNames();
1135         return this;
1136      }
1137
1138      @Override /* Overridden from Builder */
1139      public Builder useJavaBeanIntrospector() {
1140         super.useJavaBeanIntrospector();
1141         return this;
1142      }
1143
1144      @Override /* Overridden from Builder */
1145      public Builder detectRecursions() {
1146         super.detectRecursions();
1147         return this;
1148      }
1149
1150      @Override /* Overridden from Builder */
1151      public Builder detectRecursions(boolean value) {
1152         super.detectRecursions(value);
1153         return this;
1154      }
1155
1156      @Override /* Overridden from Builder */
1157      public Builder ignoreRecursions() {
1158         super.ignoreRecursions();
1159         return this;
1160      }
1161
1162      @Override /* Overridden from Builder */
1163      public Builder ignoreRecursions(boolean value) {
1164         super.ignoreRecursions(value);
1165         return this;
1166      }
1167
1168      @Override /* Overridden from Builder */
1169      public Builder initialDepth(int value) {
1170         super.initialDepth(value);
1171         return this;
1172      }
1173
1174      @Override /* Overridden from Builder */
1175      public Builder maxDepth(int value) {
1176         super.maxDepth(value);
1177         return this;
1178      }
1179
1180      @Override /* Overridden from Builder */
1181      public Builder accept(String value) {
1182         super.accept(value);
1183         return this;
1184      }
1185
1186      @Override /* Overridden from Builder */
1187      public Builder addBeanTypes() {
1188         super.addBeanTypes();
1189         return this;
1190      }
1191
1192      @Override /* Overridden from Builder */
1193      public Builder addBeanTypes(boolean value) {
1194         super.addBeanTypes(value);
1195         return this;
1196      }
1197
1198      @Override /* Overridden from Builder */
1199      public Builder addRootType() {
1200         super.addRootType();
1201         return this;
1202      }
1203
1204      @Override /* Overridden from Builder */
1205      public Builder addRootType(boolean value) {
1206         super.addRootType(value);
1207         return this;
1208      }
1209
1210      @Override /* Overridden from Builder */
1211      public Builder keepNullProperties() {
1212         super.keepNullProperties();
1213         return this;
1214      }
1215
1216      @Override /* Overridden from Builder */
1217      public Builder keepNullProperties(boolean value) {
1218         super.keepNullProperties(value);
1219         return this;
1220      }
1221
1222      @Override /* Overridden from Builder */
1223      public Builder listener(Class<? extends org.apache.juneau.serializer.SerializerListener> value) {
1224         super.listener(value);
1225         return this;
1226      }
1227
1228      @Override /* Overridden from Builder */
1229      public Builder produces(String value) {
1230         super.produces(value);
1231         return this;
1232      }
1233
1234      @Override /* Overridden from Builder */
1235      public Builder sortCollections() {
1236         super.sortCollections();
1237         return this;
1238      }
1239
1240      @Override /* Overridden from Builder */
1241      public Builder sortCollections(boolean value) {
1242         super.sortCollections(value);
1243         return this;
1244      }
1245
1246      @Override /* Overridden from Builder */
1247      public Builder sortMaps() {
1248         super.sortMaps();
1249         return this;
1250      }
1251
1252      @Override /* Overridden from Builder */
1253      public Builder sortMaps(boolean value) {
1254         super.sortMaps(value);
1255         return this;
1256      }
1257
1258      @Override /* Overridden from Builder */
1259      public Builder trimEmptyCollections() {
1260         super.trimEmptyCollections();
1261         return this;
1262      }
1263
1264      @Override /* Overridden from Builder */
1265      public Builder trimEmptyCollections(boolean value) {
1266         super.trimEmptyCollections(value);
1267         return this;
1268      }
1269
1270      @Override /* Overridden from Builder */
1271      public Builder trimEmptyMaps() {
1272         super.trimEmptyMaps();
1273         return this;
1274      }
1275
1276      @Override /* Overridden from Builder */
1277      public Builder trimEmptyMaps(boolean value) {
1278         super.trimEmptyMaps(value);
1279         return this;
1280      }
1281
1282      @Override /* Overridden from Builder */
1283      public Builder trimStrings() {
1284         super.trimStrings();
1285         return this;
1286      }
1287
1288      @Override /* Overridden from Builder */
1289      public Builder trimStrings(boolean value) {
1290         super.trimStrings(value);
1291         return this;
1292      }
1293
1294      @Override /* Overridden from Builder */
1295      public Builder uriContext(UriContext value) {
1296         super.uriContext(value);
1297         return this;
1298      }
1299
1300      @Override /* Overridden from Builder */
1301      public Builder uriRelativity(UriRelativity value) {
1302         super.uriRelativity(value);
1303         return this;
1304      }
1305
1306      @Override /* Overridden from Builder */
1307      public Builder uriResolution(UriResolution value) {
1308         super.uriResolution(value);
1309         return this;
1310      }
1311
1312      @Override /* Overridden from Builder */
1313      public Builder fileCharset(Charset value) {
1314         super.fileCharset(value);
1315         return this;
1316      }
1317
1318      @Override /* Overridden from Builder */
1319      public Builder maxIndent(int value) {
1320         super.maxIndent(value);
1321         return this;
1322      }
1323
1324      @Override /* Overridden from Builder */
1325      public Builder quoteChar(char value) {
1326         super.quoteChar(value);
1327         return this;
1328      }
1329
1330      @Override /* Overridden from Builder */
1331      public Builder quoteCharOverride(char value) {
1332         super.quoteCharOverride(value);
1333         return this;
1334      }
1335
1336      @Override /* Overridden from Builder */
1337      public Builder sq() {
1338         super.sq();
1339         return this;
1340      }
1341
1342      @Override /* Overridden from Builder */
1343      public Builder streamCharset(Charset value) {
1344         super.streamCharset(value);
1345         return this;
1346      }
1347
1348      @Override /* Overridden from Builder */
1349      public Builder useWhitespace() {
1350         super.useWhitespace();
1351         return this;
1352      }
1353
1354      @Override /* Overridden from Builder */
1355      public Builder useWhitespace(boolean value) {
1356         super.useWhitespace(value);
1357         return this;
1358      }
1359
1360      @Override /* Overridden from Builder */
1361      public Builder ws() {
1362         super.ws();
1363         return this;
1364      }
1365
1366      @Override /* Overridden from Builder */
1367      public Builder addBeanTypesXml() {
1368         super.addBeanTypesXml();
1369         return this;
1370      }
1371
1372      @Override /* Overridden from Builder */
1373      public Builder addBeanTypesXml(boolean value) {
1374         super.addBeanTypesXml(value);
1375         return this;
1376      }
1377
1378      @Override /* Overridden from Builder */
1379      public Builder addNamespaceUrisToRoot() {
1380         super.addNamespaceUrisToRoot();
1381         return this;
1382      }
1383
1384      @Override /* Overridden from Builder */
1385      public Builder addNamespaceUrisToRoot(boolean value) {
1386         super.addNamespaceUrisToRoot(value);
1387         return this;
1388      }
1389
1390      @Override /* Overridden from Builder */
1391      public Builder defaultNamespace(Namespace value) {
1392         super.defaultNamespace(value);
1393         return this;
1394      }
1395
1396      @Override /* Overridden from Builder */
1397      public Builder disableAutoDetectNamespaces() {
1398         super.disableAutoDetectNamespaces();
1399         return this;
1400      }
1401
1402      @Override /* Overridden from Builder */
1403      public Builder disableAutoDetectNamespaces(boolean value) {
1404         super.disableAutoDetectNamespaces(value);
1405         return this;
1406      }
1407
1408      @Override /* Overridden from Builder */
1409      public Builder enableNamespaces() {
1410         super.enableNamespaces();
1411         return this;
1412      }
1413
1414      @Override /* Overridden from Builder */
1415      public Builder enableNamespaces(boolean value) {
1416         super.enableNamespaces(value);
1417         return this;
1418      }
1419
1420      @Override /* Overridden from Builder */
1421      public Builder namespaces(Namespace...values) {
1422         super.namespaces(values);
1423         return this;
1424      }
1425
1426      @Override /* Overridden from Builder */
1427      public Builder ns() {
1428         super.ns();
1429         return this;
1430      }
1431
1432      @Override /* Overridden from Builder */
1433      public Builder textNodeDelimiter(String value) {
1434         super.textNodeDelimiter(value);
1435         return this;
1436      }
1437   }
1438
1439   //-------------------------------------------------------------------------------------------------------------------
1440   // Instance
1441   //-------------------------------------------------------------------------------------------------------------------
1442
1443   final AnchorText uriAnchorText;
1444   final boolean
1445      detectLabelParameters,
1446      detectLinksInStrings,
1447      addKeyValueTableHeaders,
1448      addBeanTypesHtml;
1449   final String labelParameter;
1450
1451   private final Map<ClassMeta<?>,HtmlClassMeta> htmlClassMetas = new ConcurrentHashMap<>();
1452   private final Map<BeanPropertyMeta,HtmlBeanPropertyMeta> htmlBeanPropertyMetas = new ConcurrentHashMap<>();
1453
1454   private volatile HtmlSchemaSerializer schemaSerializer;
1455
1456   /**
1457    * Constructor.
1458    *
1459    * @param builder The builder for this object.
1460    */
1461   public HtmlSerializer(Builder builder) {
1462      super(builder);
1463      detectLabelParameters = ! builder.disableDetectLabelParameters;
1464      detectLinksInStrings = ! builder.disableDetectLinksInStrings;
1465      addKeyValueTableHeaders = builder.addKeyValueTableHeaders;
1466      labelParameter = builder.labelParameter;
1467      uriAnchorText = builder.uriAnchorText;
1468      addBeanTypesHtml = builder.addBeanTypesHtml;
1469   }
1470
1471   @Override /* Context */
1472   public Builder copy() {
1473      return new Builder(this);
1474   }
1475
1476   @Override /* Context */
1477   public HtmlSerializerSession.Builder createSession() {
1478      return HtmlSerializerSession.create(this);
1479   }
1480
1481   @Override /* Context */
1482   public HtmlSerializerSession getSession() {
1483      return createSession().build();
1484   }
1485
1486   /**
1487    * Returns the schema serializer.
1488    *
1489    * @return The schema serializer.
1490    */
1491   public HtmlSerializer getSchemaSerializer() {
1492      if (schemaSerializer == null)
1493         schemaSerializer = HtmlSchemaSerializer.create().beanContext(getBeanContext()).build();
1494      return schemaSerializer;
1495   }
1496
1497   //-----------------------------------------------------------------------------------------------------------------
1498   // Extended metadata
1499   //-----------------------------------------------------------------------------------------------------------------
1500
1501   @Override /* HtmlMetaProvider */
1502   public HtmlClassMeta getHtmlClassMeta(ClassMeta<?> cm) {
1503      HtmlClassMeta m = htmlClassMetas.get(cm);
1504      if (m == null) {
1505         m = new HtmlClassMeta(cm, this);
1506         htmlClassMetas.put(cm, m);
1507      }
1508      return m;
1509   }
1510
1511   @Override /* HtmlMetaProvider */
1512   public HtmlBeanPropertyMeta getHtmlBeanPropertyMeta(BeanPropertyMeta bpm) {
1513      if (bpm == null)
1514         return HtmlBeanPropertyMeta.DEFAULT;
1515      HtmlBeanPropertyMeta m = htmlBeanPropertyMetas.get(bpm);
1516      if (m == null) {
1517         m = new HtmlBeanPropertyMeta(bpm.getDelegateFor(), this);
1518         htmlBeanPropertyMetas.put(bpm, m);
1519      }
1520      return m;
1521   }
1522
1523   //-----------------------------------------------------------------------------------------------------------------
1524   // Properties
1525   //-----------------------------------------------------------------------------------------------------------------
1526
1527   /**
1528    * Add <js>"_type"</js> properties when needed.
1529    *
1530    * @see Builder#addBeanTypesHtml()
1531    * @return
1532    *    <jk>true</jk> if <js>"_type"</js> properties will be added to beans if their type cannot be inferred
1533    *    through reflection.
1534    */
1535   @Override
1536   protected final boolean isAddBeanTypes() {
1537      return addBeanTypesHtml || super.isAddBeanTypes();
1538   }
1539
1540   /**
1541    * Add key/value headers on bean/map tables.
1542    *
1543    * @see Builder#addKeyValueTableHeaders()
1544    * @return
1545    *    <jk>true</jk> if <bc>key</bc> and <bc>value</bc> column headers are added to tables.
1546    */
1547   protected final boolean isAddKeyValueTableHeaders() {
1548      return addKeyValueTableHeaders;
1549   }
1550
1551   /**
1552    * Look for link labels in URIs.
1553    *
1554    * @see Builder#disableDetectLabelParameters()
1555    * @return
1556    *    <jk>true</jk> if we should look for URL label parameters (e.g. <js>"?label=foobar"</js>).
1557    */
1558   protected final boolean isDetectLabelParameters() {
1559      return detectLabelParameters;
1560   }
1561
1562   /**
1563    * Look for URLs in {@link String Strings}.
1564    *
1565    * @see Builder#disableDetectLinksInStrings()
1566    * @return
1567    *    <jk>true</jk> if we should automatically convert strings to URLs if they look like a URL.
1568    */
1569   protected final boolean isDetectLinksInStrings() {
1570      return detectLinksInStrings;
1571   }
1572
1573   /**
1574    * Link label parameter name.
1575    *
1576    * @see Builder#labelParameter(String)
1577    * @return
1578    *    The parameter name to look for when resolving link labels.
1579    */
1580   protected final String getLabelParameter() {
1581      return labelParameter;
1582   }
1583
1584   /**
1585    * Anchor text source.
1586    *
1587    * @see Builder#uriAnchorText(AnchorText)
1588    * @return
1589    *    When creating anchor tags (e.g. <code><xt>&lt;a</xt> <xa>href</xa>=<xs>'...'</xs>
1590    *    <xt>&gt;</xt>text<xt>&lt;/a&gt;</xt></code>) in HTML, this setting defines what to set the inner text to.
1591    */
1592   protected final AnchorText getUriAnchorText() {
1593      return uriAnchorText;
1594   }
1595
1596   //-----------------------------------------------------------------------------------------------------------------
1597   // Other methods
1598   //-----------------------------------------------------------------------------------------------------------------
1599
1600   @Override /* Context */
1601   protected JsonMap properties() {
1602      return filteredMap()
1603         .append("uriAnchorText", uriAnchorText)
1604         .append("detectLabelParameters", detectLabelParameters)
1605         .append("detectLinksInStrings", detectLinksInStrings)
1606         .append("labelParameter", labelParameter)
1607         .append("addKeyValueTableHeaders", addKeyValueTableHeaders)
1608         .append("addBeanTypesHtml", addBeanTypesHtml);
1609   }
1610}