001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau; 014 015import java.beans.*; 016import java.util.*; 017 018import static org.apache.juneau.internal.ClassUtils.*; 019import static org.apache.juneau.internal.CollectionUtils.*; 020import static org.apache.juneau.common.internal.StringUtils.*; 021import static org.apache.juneau.internal.ArrayUtils.*; 022 023import org.apache.juneau.annotation.*; 024import org.apache.juneau.cp.*; 025import org.apache.juneau.swap.*; 026 027/** 028 * Parent class for all bean filters. 029 * 030 * <p> 031 * Bean filters are used to control aspects of how beans are handled during serialization and parsing. 032 * 033 * <p> 034 * Bean filters are created by {@link Builder} which is the programmatic equivalent to the {@link Bean @Bean} 035 * annotation. 036 * 037 * <h5 class='section'>See Also:</h5><ul> 038 039 * </ul> 040 */ 041public final class BeanFilter { 042 043 //----------------------------------------------------------------------------------------------------------------- 044 // Static 045 //----------------------------------------------------------------------------------------------------------------- 046 047 /** 048 * Create a new builder for this object. 049 * 050 * @param <T> The bean class being filtered. 051 * @param beanClass The bean class being filtered. 052 * @return A new builder. 053 */ 054 public static <T> Builder create(Class<T> beanClass) { 055 return new Builder(beanClass); 056 } 057 058 //----------------------------------------------------------------------------------------------------------------- 059 // Builder 060 //----------------------------------------------------------------------------------------------------------------- 061 062 /** 063 * Builder class. 064 */ 065 public static class Builder { 066 067 Class<?> beanClass; 068 String typeName, example; 069 Set<String> 070 properties = set(), 071 excludeProperties = set(), 072 readOnlyProperties = set(), 073 writeOnlyProperties = set(); 074 Class<?> implClass, interfaceClass, stopClass; 075 boolean sortProperties, fluentSetters; 076 BeanCreator<PropertyNamer> propertyNamer = BeanCreator.of(PropertyNamer.class); 077 List<Class<?>> dictionary; 078 @SuppressWarnings("rawtypes") 079 BeanCreator<BeanInterceptor> interceptor = BeanCreator.of(BeanInterceptor.class); 080 081 /** 082 * Constructor. 083 * 084 * @param beanClass The bean class that this filter applies to. 085 */ 086 protected Builder(Class<?> beanClass) { 087 this.beanClass = beanClass; 088 } 089 090 /** 091 * Applies the information in the specified list of {@link Bean @Bean} annotations to this filter. 092 * 093 * @param annotations The annotations to apply. 094 * @return This object. 095 */ 096 public Builder applyAnnotations(List<Bean> annotations) { 097 098 annotations.forEach(x -> { 099 if (isNotEmpty(x.properties(), x.p())) properties(x.properties(), x.p()); 100 if (x.sort()) sortProperties(true); 101 if (x.findFluentSetters()) findFluentSetters(); 102 if (isNotEmpty(x.excludeProperties(), x.xp())) excludeProperties(x.excludeProperties(), x.xp()); 103 if (isNotEmpty(x.readOnlyProperties(), x.ro())) readOnlyProperties(x.readOnlyProperties(), x.ro()); 104 if (isNotEmpty(x.writeOnlyProperties(), x.wo())) writeOnlyProperties(x.writeOnlyProperties(), x.wo()); 105 if (isNotEmpty(x.typeName())) typeName(x.typeName()); 106 if (isNotVoid(x.propertyNamer())) propertyNamer(x.propertyNamer()); 107 if (isNotVoid(x.interfaceClass())) interfaceClass(x.interfaceClass()); 108 if (isNotVoid(x.stopClass())) stopClass(x.stopClass()); 109 if (isNotVoid(x.interceptor())) interceptor(x.interceptor()); 110 if (isNotVoid(x.implClass())) implClass(x.implClass()); 111 if (isNotEmptyArray(x.dictionary())) dictionary(x.dictionary()); 112 if (isNotEmpty(x.example())) example(x.example()); 113 }); 114 return this; 115 } 116 117 /** 118 * Bean dictionary type name. 119 * 120 * <p> 121 * Specifies the dictionary type name for this bean. 122 * 123 * <h5 class='section'>Example:</h5> 124 * <p class='bjava'> 125 * <jc>// Define our filter.</jc> 126 * <jk>public class</jk> MyFilter <jk>extends</jk> Builder<MyBean> { 127 * <jk>public</jk> MyFilter() { 128 * typeName(<js>"mybean"</js>); 129 * } 130 * } 131 * 132 * <jc>// Register it with a serializer or parser.</jc> 133 * WriterSerializer <jv>serializer<jv> = JsonSerializer 134 * .<jsm>create</jsm>() 135 * .beanFilters(MyFilter.<jk>class</jk>) 136 * .build(); 137 * 138 * <jc>// Produces: "{_type:'mybean', ...}"</jc> 139 * String <jv>json</jv> = <jv>serializer</jv>.serialize(<jk>new</jk> MyBean()); 140 * </p> 141 * 142 * <h5 class='section'>See Also:</h5><ul> 143 * <li class='ja'>{@link Bean#typeName()} 144 * </ul> 145 * 146 * @param value The new value for this setting. 147 * @return This object. 148 */ 149 public Builder typeName(String value) { 150 this.typeName = value; 151 return this; 152 } 153 154 /** 155 * Bean implementation class. 156 * 157 * @param value The new value for this setting. 158 * @return This object. 159 */ 160 public Builder implClass(Class<?> value) { 161 this.implClass = value; 162 return this; 163 } 164 165 /** 166 * Bean interface class. 167 * 168 * Identifies a class to be used as the interface class for this and all subclasses. 169 * 170 * <p> 171 * When specified, only the list of properties defined on the interface class will be used during serialization. 172 * <br>Additional properties on subclasses will be ignored. 173 * 174 * <p class='bjava'> 175 * <jc>// Parent class</jc> 176 * <jk>public abstract class</jk> A { 177 * <jk>public</jk> String <jf>f0</jf> = <js>"f0"</js>; 178 * } 179 * 180 * <jc>// Sub class</jc> 181 * <jk>public class</jk> A1 <jk>extends</jk> A { 182 * <jk>public</jk> String <jf>f1</jf> = <js>"f1"</js>; 183 * } 184 * 185 * <jc>// Define our filter.</jc> 186 * <jk>public class</jk> AFilter <jk>extends</jk> Builder<A> { 187 * <jk>public</jk> AFilter() { 188 * interfaceClass(A.<jk>class</jk>); 189 * } 190 * } 191 * 192 * <jc>// Register it with a serializer.</jc> 193 * WriterSerializer <jv>serializer</jv> = JsonSerializer 194 * .<jsm>create</jsm>() 195 * .beanFilters(AFilter.<jk>class</jk>) 196 * .build(); 197 * 198 * <jc>// Use it.</jc> 199 * A1 <jv>a1</jv> = <jk>new</jk> A1(); 200 * String <jv>json</jv> = <jv>serializer</jv>.serialize(<jv>a1</jv>); 201 * <jsm>assertEquals</jsm>(<js>"{f0:'f0'}"</js>, <jv>json</jv>); <jc>// Note f1 is not serialized</jc> 202 * </p> 203 * 204 * <p> 205 * Note that this filter can be used on the parent class so that it filters to all child classes, or can be set 206 * individually on the child classes. 207 * 208 * <h5 class='section'>See Also:</h5><ul> 209 * <li class='ja'>{@link Bean#interfaceClass()} 210 * </ul> 211 * 212 * @param value The new value for this setting. 213 * @return This object. 214 */ 215 public Builder interfaceClass(Class<?> value) { 216 this.interfaceClass = value; 217 return this; 218 } 219 220 /** 221 * Bean stop class. 222 * 223 * <p> 224 * Identifies a stop class for this class and all subclasses. 225 * 226 * <p> 227 * Identical in purpose to the stop class specified by {@link Introspector#getBeanInfo(Class, Class)}. 228 * <br>Any properties in the stop class or in its base classes will be ignored during analysis. 229 * 230 * <p> 231 * For example, in the following class hierarchy, instances of <c>C3</c> will include property <c>p3</c>, 232 * but not <c>p1</c> or <c>p2</c>. 233 * 234 * <h5 class='section'>Example:</h5> 235 * <p class='bjava'> 236 * <jk>public class</jk> C1 { 237 * <jk>public int</jk> getP1(); 238 * } 239 * 240 * <jk>public class</jk> C2 <jk>extends</jk> C1 { 241 * <jk>public int</jk> getP2(); 242 * } 243 * 244 * <jk>public class</jk> C3 <jk>extends</jk> C2 { 245 * <jk>public int</jk> getP3(); 246 * } 247 * 248 * <jc>// Define our filter.</jc> 249 * <jk>public class</jk> C3Filter <jk>extends</jk> Builder<C3> { 250 * <jk>public</jk> C3Filter() { 251 * stopClass(C2.<jk>class</jk>); 252 * } 253 * } 254 * 255 * <jc>// Register it with a serializer.</jc> 256 * WriterSerializer <jv>serializer</jv> = JsonSerializer 257 * .<jsm>create</jsm>() 258 * .beanFilters(C3Filter.<jk>class</jk>) 259 * .build(); 260 * 261 * <jc>// Serializes property 'p3', but NOT 'p1' or 'p2'.</jc> 262 * String <jv>json</jv> = <jv>serializer</jv>.serialize(<jk>new</jk> C3()); 263 * </p> 264 * 265 * <h5 class='section'>See Also:</h5><ul> 266 * <li class='ja'>{@link Bean#stopClass()} 267 * </ul> 268 * 269 * @param value The new value for this setting. 270 * @return This object. 271 */ 272 public Builder stopClass(Class<?> value) { 273 this.stopClass = value; 274 return this; 275 } 276 277 /** 278 * Sort bean properties. 279 * 280 * <p> 281 * When <jk>true</jk>, all bean properties will be serialized and access in alphabetical order. 282 * <br>Otherwise, the natural order of the bean properties is used which is dependent on the JVM vendor. 283 * 284 * <h5 class='section'>Example:</h5> 285 * <p class='bjava'> 286 * <jc>// Define our filter.</jc> 287 * <jk>public class</jk> MyFilter <jk>extends</jk> Builder<MyBean> { 288 * <jk>public</jk> MyFilter() { 289 * sortProperties(); 290 * } 291 * } 292 * 293 * <jc>// Register it with a serializer.</jc> 294 * WriterSerializer <jv>serializer</jv> = JsonSerializer 295 * .<jsm>create</jsm>() 296 * .beanFilters(MyFilter.<jk>class</jk>) 297 * .build(); 298 * 299 * <jc>// Properties will be sorted alphabetically.</jc> 300 * String <jv>json</jv> = <jv>serializer</jv>.serialize(<jk>new</jk> MyBean()); 301 * </p> 302 * 303 * <h5 class='section'>See Also:</h5><ul> 304 * <li class='ja'>{@link Bean#sort()} 305 * <li class='jf'>{@link BeanContext.Builder#sortProperties()} 306 * </ul> 307 * 308 * @param value 309 * The new value for this property. 310 * <br>The default is <jk>false</jk>. 311 * @return This object. 312 */ 313 public Builder sortProperties(boolean value) { 314 this.sortProperties = value; 315 return this; 316 } 317 318 /** 319 * Sort bean properties. 320 * 321 * <p> 322 * Shortcut for calling <code>sortProperties(<jk>true</jk>)</code>. 323 * 324 * <h5 class='section'>See Also:</h5><ul> 325 * <li class='ja'>{@link Bean#sort()} 326 * <li class='jf'>{@link BeanContext.Builder#sortProperties()} 327 * </ul> 328 * 329 * @return This object. 330 */ 331 public Builder sortProperties() { 332 this.sortProperties = true; 333 return this; 334 } 335 336 /** 337 * Find fluent setters. 338 * 339 * <p> 340 * When enabled, fluent setters are detected on beans. 341 * 342 * <p> 343 * Fluent setters must have the following attributes: 344 * <ul> 345 * <li>Public. 346 * <li>Not static. 347 * <li>Take in one parameter. 348 * <li>Return the bean itself. 349 * </ul> 350 * 351 * <h5 class='section'>Example:</h5> 352 * <p class='bjava'> 353 * <jc>// Define our filter.</jc> 354 * <jk>public class</jk> MyFilter <jk>extends</jk> Builder<MyBean> { 355 * <jk>public</jk> MyFilter() { 356 * findFluentSetters(); 357 * } 358 * } 359 * </p> 360 * 361 * <h5 class='section'>See Also:</h5><ul> 362 * <li class='ja'>{@link Bean#findFluentSetters()} 363 * <li class='jm'>{@link BeanContext.Builder#findFluentSetters()} 364 * </ul> 365 * 366 * @return This object. 367 */ 368 public Builder findFluentSetters() { 369 this.fluentSetters = true; 370 return this; 371 } 372 373 /** 374 * Bean property namer 375 * 376 * <p> 377 * The class to use for calculating bean property names. 378 * 379 * <h5 class='section'>Example:</h5> 380 * <p class='bjava'> 381 * <jc>// Define our filter.</jc> 382 * <jk>public class</jk> MyFilter <jk>extends</jk> Builder<MyBean> { 383 * <jk>public</jk> MyFilter() { 384 * <jc>// Use Dashed-Lower-Case property names.</jc> 385 * <jc>// (e.g. "foo-bar-url" instead of "fooBarURL")</jc> 386 * propertyNamer(PropertyNamerDLC.<jk>class</jk>); 387 * } 388 * } 389 * 390 * <jc>// Register it with a serializer or parser.</jc> 391 * WriterSerializer <jv>serializer</jv> = JsonSerializer 392 * .<jsm>create</jsm>() 393 * .beanFilters(MyFilter.<jk>class</jk>) 394 * .build(); 395 * 396 * <jc>// Properties names will be Dashed-Lower-Case.</jc> 397 * String <jv>json</jv> = <jv>serializer</jv>.serialize(<jk>new</jk> MyBean()); 398 * </p> 399 * 400 * <h5 class='section'>See Also:</h5><ul> 401 * <li class='ja'>{@link Bean#propertyNamer()} 402 * <li class='jm'>{@link BeanContext.Builder#propertyNamer(Class)} 403 * <li class='jc'>{@link PropertyNamer} 404 * </ul> 405 * 406 * @param value 407 * The new value for this setting. 408 * <br>The default is {@link BasicPropertyNamer}. 409 * @return This object. 410 */ 411 public Builder propertyNamer(Class<? extends PropertyNamer> value) { 412 this.propertyNamer.type(value); 413 return this; 414 } 415 416 /** 417 * Bean property includes. 418 * 419 * <p> 420 * Specifies the set and order of names of properties associated with the bean class. 421 * 422 * <h5 class='section'>Example:</h5> 423 * <p class='bjava'> 424 * <jc>// Define our filter.</jc> 425 * <jk>public class</jk> MyFilter <jk>extends</jk> Builder<MyBean> { 426 * <jk>public</jk> MyFilter() { 427 * properties(<js>"foo,bar,baz"</js>); 428 * } 429 * } 430 * 431 * <jc>// Register it with a serializer.</jc> 432 * WriterSerializer <jv>serializer</jv> = JsonSerializer 433 * .<jsm>create</jsm>() 434 * .beanFilters(MyFilter.<jk>class</jk>) 435 * .build(); 436 * 437 * <jc>// Only serializes the properties 'foo', 'bar', and 'baz'.</jc> 438 * String <jv>json</jv> = <jv>serializer</jv>.serialize(<jk>new</jk> MyBean()); 439 * </p> 440 * 441 * <h5 class='section'>See Also:</h5><ul> 442 * <li class='ja'>{@link Bean#properties()} 443 * <li class='jm'>{@link BeanContext.Builder#beanProperties(Class, String)} 444 * <li class='jm'>{@link BeanContext.Builder#beanProperties(String, String)} 445 * <li class='jm'>{@link BeanContext.Builder#beanProperties(Map)} 446 * </ul> 447 * 448 * @param value 449 * The new value for this setting. 450 * <br>Values can contain comma-delimited list of property names. 451 * @return This object. 452 */ 453 public Builder properties(String...value) { 454 this.properties = set(); 455 for (String v : value) 456 split(v, x -> properties.add(x)); 457 return this; 458 } 459 460 /** 461 * Bean property excludes. 462 * 463 * <p> 464 * Specifies properties to exclude from the bean class. 465 * 466 * <h5 class='section'>Example:</h5> 467 * <p class='bjava'> 468 * <jc>// Define our filter.</jc> 469 * <jk>public class</jk> MyFilter <jk>extends</jk> Builder<MyBean> { 470 * <jk>public</jk> MyFilter() { 471 * excludeProperties(<js>"foo,bar"</js>); 472 * } 473 * } 474 * 475 * <jc>// Register it with a serializer.</jc> 476 * WriterSerializer <jv>serializer</jv> = JsonSerializer 477 * .<jsm>create</jsm>() 478 * .beanFilters(MyFilter.<jk>class</jk>) 479 * .build(); 480 * 481 * <jc>// Serializes all properties except for 'foo' and 'bar'.</jc> 482 * String <jv>json</jv> = <jv>serializer</jv>.serialize(<jk>new</jk> MyBean()); 483 * </p> 484 * 485 * <h5 class='section'>See Also:</h5><ul> 486 * <li class='ja'>{@link Bean#excludeProperties()} 487 * <li class='jm'>{@link BeanContext.Builder#beanPropertiesExcludes(Class, String)} 488 * <li class='jm'>{@link BeanContext.Builder#beanPropertiesExcludes(String, String)} 489 * <li class='jm'>{@link BeanContext.Builder#beanPropertiesExcludes(Map)} 490 * </ul> 491 * 492 * @param value 493 * The new value for this setting. 494 * <br>Values can contain comma-delimited list of property names. 495 * @return This object. 496 */ 497 public Builder excludeProperties(String...value) { 498 this.excludeProperties = set(); 499 for (String v : value) 500 split(v, x -> excludeProperties.add(x)); 501 return this; 502 } 503 504 /** 505 * Read-only bean properties. 506 * 507 * <p> 508 * Specifies one or more properties on a bean that are read-only despite having valid getters. 509 * Serializers will serialize such properties as usual, but parsers will silently ignore them. 510 * 511 * <h5 class='section'>Example:</h5> 512 * <p class='bjava'> 513 * <jc>// Define our filter.</jc> 514 * <jk>public class</jk> MyFilter <jk>extends</jk> Builder<MyBean> { 515 * <jk>public</jk> MyFilter() { 516 * readOnlyProperties(<js>"foo,bar"</js>); 517 * } 518 * } 519 * 520 * <jc>// Register it with a parser.</jc> 521 * ReaderParser <jv>parser</jv> = JsonParser 522 * .<jsm>create</jsm>() 523 * .beanFilters(MyFilter.<jk>class</jk>) 524 * .build(); 525 * 526 * <jc>// Parsers all properties except for 'foo' and 'bar'.</jc> 527 * MyBean <jv>bean</jv> = <jv>parser</jv>.parse(<js>"..."</js>, MyBean.<jk>class</jk>); 528 * </p> 529 * 530 * <h5 class='section'>See Also:</h5><ul> 531 * <li class='ja'>{@link Bean#readOnlyProperties()} 532 * <li class='ja'>{@link Beanp#ro()} 533 * <li class='jm'>{@link BeanContext.Builder#beanPropertiesReadOnly(Class, String)} 534 * <li class='jm'>{@link BeanContext.Builder#beanPropertiesReadOnly(String, String)} 535 * <li class='jm'>{@link BeanContext.Builder#beanPropertiesReadOnly(Map)} 536 * </ul> 537 * 538 * @param value 539 * The new value for this setting. 540 * <br>Values can contain comma-delimited list of property names. 541 * @return This object. 542 */ 543 public Builder readOnlyProperties(String...value) { 544 this.readOnlyProperties = set(); 545 for (String v : value) 546 split(v, x -> readOnlyProperties.add(x)); 547 return this; 548 } 549 550 /** 551 * Write-only bean properties. 552 * 553 * <p> 554 * Specifies one or more properties on a bean that are write-only despite having valid setters. 555 * Parsers will parse such properties as usual, but serializers will silently ignore them. 556 * 557 * <h5 class='section'>Example:</h5> 558 * <p class='bjava'> 559 * <jc>// Define our filter.</jc> 560 * <jk>public class</jk> MyFilter <jk>extends</jk> Builder<MyBean> { 561 * <jk>public</jk> MyFilter() { 562 * writeOnlyProperties(<js>"foo,bar"</js>); 563 * } 564 * } 565 * 566 * <jc>// Register it with a serializer.</jc> 567 * WriterSerializer <jv>serializer</jv> = JsonSerializer 568 * .<jsm>create</jsm>() 569 * .beanFilters(MyFilter.<jk>class</jk>) 570 * .build(); 571 * 572 * <jc>// Serializes all properties except for 'foo' and 'bar'.</jc> 573 * String <jv>json</jv> = <jv>serializer</jv>.serialize(<jk>new</jk> MyBean()); 574 * </p> 575 * 576 * <h5 class='section'>See Also:</h5><ul> 577 * <li class='ja'>{@link Bean#writeOnlyProperties()} 578 * <li class='ja'>{@link Beanp#wo()} 579 * <li class='jm'>{@link BeanContext.Builder#beanPropertiesWriteOnly(Class, String)} 580 * <li class='jm'>{@link BeanContext.Builder#beanPropertiesWriteOnly(String, String)} 581 * <li class='jm'>{@link BeanContext.Builder#beanPropertiesWriteOnly(Map)} 582 * </ul> 583 * 584 * @param value 585 * The new value for this setting. 586 * <br>Values can contain comma-delimited list of property names. 587 * @return This object. 588 */ 589 public Builder writeOnlyProperties(String...value) { 590 this.writeOnlyProperties = set(); 591 for (String v : value) 592 split(v, x -> writeOnlyProperties.add(x)); 593 return this; 594 } 595 596 /** 597 * Bean dictionary. 598 * 599 * <p> 600 * Adds to the list of classes that make up the bean dictionary for this bean. 601 * 602 * <h5 class='section'>Example:</h5> 603 * <p class='bjava'> 604 * <jc>// Define our filter.</jc> 605 * <jk>public class</jk> MyFilter <jk>extends</jk> Builder<MyBean> { 606 * <jk>public</jk> MyFilter() { 607 * <jc>// Our bean contains generic collections of Foo and Bar objects.</jc> 608 * beanDictionary(Foo.<jk>class</jk>, Bar.<jk>class</jk>); 609 * } 610 * } 611 * 612 * <jc>// Register it with a parser.</jc> 613 * ReaderParser <jv>parser</jv> = JsonParser 614 * .<jsm>create</jsm>() 615 * .beanFilters(MyFilter.<jk>class</jk>) 616 * .build(); 617 * 618 * <jc>// Instantiate our bean.</jc> 619 * MyBean <jv>bean</jv> = <jv>parser</jv>.parse(<jv>json</jv>); 620 * </p> 621 * 622 * <h5 class='section'>See Also:</h5><ul> 623 * <li class='ja'>{@link Bean#dictionary()} 624 * <li class='jm'>{@link BeanContext.Builder#beanDictionary(Class...)} 625 * </ul> 626 * 627 * @param values 628 * The values to add to this property. 629 * @return This object. 630 */ 631 public Builder dictionary(Class<?>...values) { 632 if (dictionary == null) 633 dictionary = list(values); 634 else for (Class<?> cc : values) 635 dictionary.add(cc); 636 return this; 637 } 638 639 /** 640 * Example. 641 * 642 * @param value 643 * The new value for this property. 644 * @return This object. 645 */ 646 public Builder example(String value) { 647 this.example = value; 648 return this; 649 } 650 651 /** 652 * Bean interceptor. 653 * 654 * <p> 655 * The interceptor to use for intercepting and altering getter and setter calls. 656 * 657 * <h5 class='section'>Example:</h5> 658 * <p class='bjava'> 659 * <jc>// Define our filter.</jc> 660 * <jk>public class</jk> MyFilter <jk>extends</jk> Builder<MyBean> { 661 * <jk>public</jk> MyFilter() { 662 * <jc>// Our bean contains generic collections of Foo and Bar objects.</jc> 663 * interceptor(AddressInterceptor.<jk>class</jk>); 664 * } 665 * } 666 * 667 * <jc>// Register it with a serializer or parser.</jc> 668 * WriterSerializer <jv>serializer</jv> = JsonSerializer 669 * .<jsm>create</jsm>() 670 * .beanFilters(MyFilter.<jk>class</jk>) 671 * .build(); 672 * </p> 673 * 674 * <h5 class='section'>See Also:</h5><ul> 675 * <li class='ja'>{@link Bean#interceptor()} 676 * <li class='jc'>{@link BeanInterceptor} 677 * </ul> 678 * 679 * @param value 680 * The new value for this setting. 681 * <br>The default value is {@link BeanInterceptor}. 682 * @return This object. 683 */ 684 public Builder interceptor(Class<?> value) { 685 this.interceptor.type(value); 686 return this; 687 } 688 689 /** 690 * Creates a {@link BeanFilter} with settings in this builder class. 691 * 692 * @return A new {@link BeanFilter} instance. 693 */ 694 public BeanFilter build() { 695 return new BeanFilter(this); 696 } 697 } 698 699 //----------------------------------------------------------------------------------------------------------------- 700 // Instance 701 //----------------------------------------------------------------------------------------------------------------- 702 703 private final Class<?> beanClass; 704 private final Set<String> properties, excludeProperties, readOnlyProperties, writeOnlyProperties; 705 private final PropertyNamer propertyNamer; 706 private final Class<?> implClass, interfaceClass, stopClass; 707 private final boolean sortProperties, fluentSetters; 708 private final String typeName, example; 709 private final Class<?>[] beanDictionary; 710 @SuppressWarnings("rawtypes") 711 private final BeanInterceptor interceptor; 712 713 /** 714 * Constructor. 715 */ 716 BeanFilter(Builder builder) { 717 this.beanClass = builder.beanClass; 718 this.typeName = builder.typeName; 719 this.properties = copyOf(builder.properties); 720 this.excludeProperties = copyOf(builder.excludeProperties); 721 this.readOnlyProperties = copyOf(builder.readOnlyProperties); 722 this.writeOnlyProperties = copyOf(builder.writeOnlyProperties); 723 this.example = builder.example; 724 this.implClass = builder.implClass; 725 this.interfaceClass = builder.interfaceClass; 726 this.stopClass = builder.stopClass; 727 this.sortProperties = builder.sortProperties; 728 this.fluentSetters = builder.fluentSetters; 729 this.propertyNamer = builder.propertyNamer.orElse(null); 730 this.beanDictionary = 731 builder.dictionary == null 732 ? null 733 : builder.dictionary.toArray(new Class<?>[builder.dictionary.size()]); 734 this.interceptor = builder.interceptor.orElse(BeanInterceptor.DEFAULT); 735 } 736 737 /** 738 * Returns the bean class that this filter applies to. 739 * 740 * @return The bean class that this filter applies to. 741 */ 742 public Class<?> getBeanClass() { 743 return beanClass; 744 } 745 746 /** 747 * Returns the dictionary name associated with this bean. 748 * 749 * @return The dictionary name associated with this bean, or <jk>null</jk> if no name is defined. 750 */ 751 public String getTypeName() { 752 return typeName; 753 } 754 755 /** 756 * Returns the bean dictionary defined on this bean. 757 * 758 * @return The bean dictionary defined on this bean, or <jk>null</jk> if no bean dictionary is defined. 759 */ 760 public Class<?>[] getBeanDictionary() { 761 return beanDictionary; 762 } 763 764 /** 765 * Returns the set and order of names of properties associated with a bean class. 766 * 767 * @return 768 * The names of the properties associated with a bean class, or and empty set if all bean properties should 769 * be used. 770 */ 771 public Set<String> getProperties() { 772 return properties; 773 } 774 775 /** 776 * Returns the list of properties to ignore on a bean. 777 * 778 * @return The names of the properties to ignore on a bean, or an empty set to not ignore any properties. 779 */ 780 public Set<String> getExcludeProperties() { 781 return excludeProperties; 782 } 783 784 /** 785 * Returns the list of read-only properties on a bean. 786 * 787 * @return The names of the read-only properties on a bean, or an empty set to not have any read-only properties. 788 */ 789 public Set<String> getReadOnlyProperties() { 790 return readOnlyProperties; 791 } 792 793 /** 794 * Returns the list of write-only properties on a bean. 795 * 796 * @return The names of the write-only properties on a bean, or an empty set to not have any write-only properties. 797 */ 798 public Set<String> getWriteOnlyProperties() { 799 return writeOnlyProperties; 800 } 801 802 /** 803 * Returns <jk>true</jk> if the properties defined on this bean class should be ordered alphabetically. 804 * 805 * <p> 806 * This method is only used when the {@link #getProperties()} method returns <jk>null</jk>. 807 * Otherwise, the ordering of the properties in the returned value is used. 808 * 809 * @return <jk>true</jk> if bean properties should be sorted. 810 */ 811 public boolean isSortProperties() { 812 return sortProperties; 813 } 814 815 /** 816 * Returns <jk>true</jk> if we should find fluent setters. 817 * 818 * @return <jk>true</jk> if fluent setters should be found. 819 */ 820 public boolean isFluentSetters() { 821 return fluentSetters; 822 } 823 824 /** 825 * Returns the {@link PropertyNamer} associated with the bean to tailor the names of bean properties. 826 * 827 * @return The property namer class, or <jk>null</jk> if no property namer is associated with this bean property. 828 */ 829 public PropertyNamer getPropertyNamer() { 830 return propertyNamer; 831 } 832 833 /** 834 * Returns the implementation class associated with this class. 835 * 836 * @return The implementation class associated with this class, or <jk>null</jk> if no implementation class is associated. 837 */ 838 public Class<?> getImplClass() { 839 return implClass; 840 } 841 842 /** 843 * Returns the interface class associated with this class. 844 * 845 * @return The interface class associated with this class, or <jk>null</jk> if no interface class is associated. 846 */ 847 public Class<?> getInterfaceClass() { 848 return interfaceClass; 849 } 850 851 /** 852 * Returns the stop class associated with this class. 853 * 854 * @return The stop class associated with this class, or <jk>null</jk> if no stop class is associated. 855 */ 856 public Class<?> getStopClass() { 857 return stopClass; 858 } 859 860 /** 861 * Returns the example associated with this class. 862 * 863 * @return The example associated with this class, or <jk>null</jk> if no example is associated. 864 */ 865 public String getExample() { 866 return example; 867 } 868 869 /** 870 * Calls the {@link BeanInterceptor#readProperty(Object, String, Object)} method on the registered property filters. 871 * 872 * @param bean The bean from which the property was read. 873 * @param name The property name. 874 * @param value The value just extracted from calling the bean getter. 875 * @return The value to serialize. Default is just to return the existing value. 876 */ 877 @SuppressWarnings("unchecked") 878 public Object readProperty(Object bean, String name, Object value) { 879 return interceptor.readProperty(bean, name, value); 880 } 881 882 /** 883 * Calls the {@link BeanInterceptor#writeProperty(Object, String, Object)} method on the registered property filters. 884 * 885 * @param bean The bean from which the property was read. 886 * @param name The property name. 887 * @param value The value just parsed. 888 * @return The value to serialize. Default is just to return the existing value. 889 */ 890 @SuppressWarnings("unchecked") 891 public Object writeProperty(Object bean, String name, Object value) { 892 return interceptor.writeProperty(bean, name, value); 893 } 894}