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