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