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&lt;MyBean&gt; {
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&lt;A&gt; {
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&lt;C3&gt; {
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&lt;MyBean&gt; {
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&lt;MyBean&gt; {
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&lt;MyBean&gt; {
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&lt;MyBean&gt; {
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&lt;MyBean&gt; {
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&lt;MyBean&gt; {
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&lt;MyBean&gt; {
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&lt;MyBean&gt; {
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&lt;MyBean&gt; {
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}