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.transform;
014
015import static org.apache.juneau.internal.ClassUtils.*;
016
017import java.beans.*;
018import java.util.*;
019
020import org.apache.juneau.*;
021import org.apache.juneau.annotation.*;
022
023/**
024 * Builder class for {@link BeanFilter} objects.
025 *
026 * <p>
027 * This class is the programmatic equivalent to the {@link Bean @Bean} annotation.
028 *
029 * <p>
030 * The general approach to defining bean filters is to create subclasses from this class and call methods to
031 * set various attributes
032 * <p class='bcode w800'>
033 *    <jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
034 *
035 *       <jc>// Must provide a no-arg constructor!</jc>
036 *       <jk>public</jk> MyFilter() {
037 *
038 *          <jc>// Call one or more configuration methods.</jc>
039 *          includeProperties(<js>"foo,bar,baz"</js>);
040 *          sortProperties();
041 *          propertyNamer(PropertyNamerULC.<jk>class</jk>);
042 *       }
043 *    }
044 *
045 *    <jc>// Register it with a serializer or parser.</jc>
046 *    WriterSerializer s = JsonSerializer
047 *       .<jsm>create</jsm>()
048 *       .beanFilters(MyFilter.<jk>class</jk>)
049 *       .build();
050 * </p>
051 *
052 * <h5 class='section'>See Also:</h5>
053 * <ul>
054 *    <li class='link'>{@doc juneau-marshall.Transforms.BeanFilters}
055 * </ul>
056 *
057 * @param <T> The bean type that this filter applies to.
058 */
059public class BeanFilterBuilder<T> {
060
061   Class<?> beanClass;
062   String typeName;
063   String[] includeProperties, excludeProperties;
064   Class<?> interfaceClass, stopClass;
065   boolean sortProperties, fluentSetters;
066   Object propertyNamer;
067   List<Class<?>> beanDictionary;
068   Object propertyFilter;
069
070   /**
071    * Constructor.
072    *
073    * <p>
074    * Bean class is determined through reflection of the parameter type.
075    */
076   protected BeanFilterBuilder() {
077      beanClass = resolveParameterType(BeanFilterBuilder.class, 0, this.getClass());
078   }
079
080   /**
081    * Constructor.
082    *
083    * @param beanClass The bean class that this filter applies to.
084    */
085   protected BeanFilterBuilder(Class<?> beanClass) {
086      this.beanClass = beanClass;
087   }
088
089   /**
090    * Configuration property:  Bean dictionary type name.
091    *
092    * <p>
093    * Specifies the dictionary type name for this bean.
094    *
095    * <h5 class='section'>Example:</h5>
096    * <p class='bcode w800'>
097    *    <jc>// Define our filter.</jc>
098    *    <jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
099    *       <jk>public</jk> MyFilter() {
100    *          typeName(<js>"mybean"</js>);
101    *       }
102    *    }
103    *
104    *    <jc>// Register it with a serializer or parser.</jc>
105    *    WriterSerializer s = JsonSerializer
106    *       .<jsm>create</jsm>()
107    *       .beanFilters(MyFilter.<jk>class</jk>)
108    *       .build();
109    *
110    *    <jc>// Produces:  "{_type:'mybean', ...}"</jc>
111    *    String json = s.serialize(<jk>new</jk> MyBean());
112    * </p>
113    *
114    * <h5 class='section'>See Also:</h5>
115    * <ul>
116    *    <li class='ja'>{@link Bean#typeName()}
117    * </ul>
118    *
119    * @param value The new value for this setting.
120    * @return This object (for method chaining).
121    */
122   public BeanFilterBuilder<T> typeName(String value) {
123      this.typeName = value;
124      return this;
125   }
126
127   /**
128    * Configuration property:  Bean property includes.
129    *
130    * <p>
131    * Specifies the set and order of names of properties associated with the bean class.
132    *
133    * <h5 class='section'>Example:</h5>
134    * <p class='bcode w800'>
135    *    <jc>// Define our filter.</jc>
136    *    <jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
137    *       <jk>public</jk> MyFilter() {
138    *          includeProperties(<js>"foo,bar,baz"</js>);
139    *       }
140    *    }
141    *
142    *    <jc>// Register it with a serializer.</jc>
143    *    WriterSerializer s = JsonSerializer
144    *       .<jsm>create</jsm>()
145    *       .beanFilters(MyFilter.<jk>class</jk>)
146    *       .build();
147    *
148    *    <jc>// Only serializes the properties 'foo', 'bar', and 'baz'.</jc>
149    *    String json = s.serialize(<jk>new</jk> MyBean());
150    * </p>
151    *
152    * <h5 class='section'>See Also:</h5>
153    * <ul>
154    *    <li class='ja'>{@link Bean#properties()}
155    *    <li class='jf'>{@link BeanContext#BEAN_includeProperties}
156    * </ul>
157    *
158    * @param value
159    *    The new value for this setting.
160    *    <br>Values can contain comma-delimited list of property names.
161    * @return This object (for method chaining).
162    */
163   public BeanFilterBuilder<T> properties(String...value) {
164      this.includeProperties = value;
165      return this;
166   }
167
168   /**
169    * Configuration property:  Bean property excludes.
170    *
171    * <p>
172    * Specifies properties to exclude from the bean class.
173    *
174    * <h5 class='section'>Example:</h5>
175    * <p class='bcode w800'>
176    *    <jc>// Define our filter.</jc>
177    *    <jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
178    *       <jk>public</jk> MyFilter() {
179    *          excludeProperties(<js>"foo,bar"</js>);
180    *       }
181    *    }
182    *
183    *    <jc>// Register it with a serializer.</jc>
184    *    WriterSerializer s = JsonSerializer
185    *       .<jsm>create</jsm>()
186    *       .beanFilters(MyFilter.<jk>class</jk>)
187    *       .build();
188    *
189    *    <jc>// Serializes all properties except for 'foo' and 'bar'.</jc>
190    *    String json = s.serialize(<jk>new</jk> MyBean());
191    * </p>
192    *
193    * <h5 class='section'>See Also:</h5>
194    * <ul>
195    *    <li class='ja'>{@link Bean#excludeProperties()}
196    *    <li class='jf'>{@link BeanContext#BEAN_excludeProperties}
197    * </ul>
198    *
199    * @param value
200    *    The new value for this setting.
201    *    <br>Values can contain comma-delimited list of property names.
202    * @return This object (for method chaining).
203    */
204   public BeanFilterBuilder<T> excludeProperties(String...value) {
205      this.excludeProperties = value;
206      return this;
207   }
208
209   /**
210    * Configuration property:  Bean interface class.
211    *
212    * Identifies a class to be used as the interface class for this and all subclasses.
213    *
214    * <p>
215    * When specified, only the list of properties defined on the interface class will be used during serialization.
216    * <br>Additional properties on subclasses will be ignored.
217    *
218    * <p class='bcode w800'>
219    *    <jc>// Parent class</jc>
220    *    <jk>public abstract class</jk> A {
221    *       <jk>public</jk> String <jf>f0</jf> = <js>"f0"</js>;
222    *    }
223    *
224    *    <jc>// Sub class</jc>
225    *    <jk>public class</jk> A1 <jk>extends</jk> A {
226    *       <jk>public</jk> String <jf>f1</jf> = <js>"f1"</js>;
227    *    }
228    *
229    *    <jc>// Define our filter.</jc>
230    *    <jk>public class</jk> AFilter <jk>extends</jk> BeanFilterBuilder&lt;A&gt; {
231    *       <jk>public</jk> AFilter() {
232    *          interfaceClass(A.<jk>class</jk>);
233    *       }
234    *    }
235    *
236    *    <jc>// Register it with a serializer.</jc>
237    *    WriterSerializer s = JsonSerializer
238    *       .<jsm>create</jsm>()
239    *       .beanFilters(AFilter.<jk>class</jk>)
240    *       .build();
241    *
242    *    <jc>// Use it.</jc>
243    *    A1 a1 = <jk>new</jk> A1();
244    *    String r = s.serialize(a1);
245    *    <jsm>assertEquals</jsm>(<js>"{f0:'f0'}"</js>, r);  <jc>// Note f1 is not serialized</jc>
246    * </p>
247    *
248    * <p>
249    * Note that this filter can be used on the parent class so that it filters to all child classes, or can be set
250    * individually on the child classes.
251    *
252    * <h5 class='section'>See Also:</h5>
253    * <ul>
254    *    <li class='ja'>{@link Bean#interfaceClass()}
255    *    <li class='jf'>{@link BeanContext#BEAN_beanFilters}
256    * </ul>
257    *
258    * @param value The new value for this setting.
259    * @return This object (for method chaining).
260    */
261   public BeanFilterBuilder<T> interfaceClass(Class<?> value) {
262      this.interfaceClass = value;
263      return this;
264   }
265
266   /**
267    * Configuration property:  Bean stop class.
268    *
269    * <p>
270    * Identifies a stop class for this class and all subclasses.
271    *
272    * <p>
273    * Identical in purpose to the stop class specified by {@link Introspector#getBeanInfo(Class, Class)}.
274    * <br>Any properties in the stop class or in its base classes will be ignored during analysis.
275    *
276    * <p>
277    * For example, in the following class hierarchy, instances of <code>C3</code> will include property <code>p3</code>,
278    * but not <code>p1</code> or <code>p2</code>.
279    *
280    * <h5 class='section'>Example:</h5>
281    * <p class='bcode w800'>
282    *    <jk>public class</jk> C1 {
283    *       <jk>public int</jk> getP1();
284    *    }
285    *
286    *    <jk>public class</jk> C2 <jk>extends</jk> C1 {
287    *       <jk>public int</jk> getP2();
288    *    }
289    *
290    *    <jk>public class</jk> C3 <jk>extends</jk> C2 {
291    *       <jk>public int</jk> getP3();
292    *    }
293    *
294    *    <jc>// Define our filter.</jc>
295    *    <jk>public class</jk> C3Filter <jk>extends</jk> BeanFilterBuilder&lt;C3&gt; {
296    *       <jk>public</jk> C3Filter() {
297    *          stopClass(C2.<jk>class</jk>);
298    *       }
299    *    }
300    *
301    *    <jc>// Register it with a serializer.</jc>
302    *    WriterSerializer s = JsonSerializer
303    *       .<jsm>create</jsm>()
304    *       .beanFilters(C3Filter.<jk>class</jk>)
305    *       .build();
306    *
307    *    <jc>// Serializes property 'p3', but NOT 'p1' or 'p2'.</jc>
308    *    String json = s.serialize(<jk>new</jk> C3());
309    * </p>
310    *
311    * <h5 class='section'>See Also:</h5>
312    * <ul>
313    *    <li class='ja'>{@link Bean#stopClass()}
314    * </ul>
315    *
316    * @param value The new value for this setting.
317    * @return This object (for method chaining).
318    */
319   public BeanFilterBuilder<T> stopClass(Class<?> value) {
320      this.stopClass = value;
321      return this;
322   }
323
324   /**
325    * Configuration property:  Sort bean properties.
326    *
327    * <p>
328    * When <jk>true</jk>, all bean properties will be serialized and access in alphabetical order.
329    * <br>Otherwise, the natural order of the bean properties is used which is dependent on the JVM vendor.
330    *
331    * <h5 class='section'>Example:</h5>
332    * <p class='bcode w800'>
333    *    <jc>// Define our filter.</jc>
334    *    <jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
335    *       <jk>public</jk> MyFilter() {
336    *          sortProperties();
337    *       }
338    *    }
339    *
340    *    <jc>// Register it with a serializer.</jc>
341    *    WriterSerializer s = JsonSerializer
342    *       .<jsm>create</jsm>()
343    *       .beanFilters(MyFilter.<jk>class</jk>)
344    *       .build();
345    *
346    *    <jc>// Properties will be sorted alphabetically.</jc>
347    *    String json = s.serialize(<jk>new</jk> MyBean());
348    * </p>
349    *
350    * <h5 class='section'>See Also:</h5>
351    * <ul>
352    *    <li class='ja'>{@link Bean#sort()}
353    *    <li class='jf'>{@link BeanContext#BEAN_sortProperties}
354    * </ul>
355    *
356    * @param value
357    *    The new value for this property.
358    *    <br>The default is <jk>false</jk>.
359    * @return This object (for method chaining).
360    */
361   public BeanFilterBuilder<T> sortProperties(boolean value) {
362      this.sortProperties = value;
363      return this;
364   }
365
366   /**
367    * Configuration property:  Sort bean properties.
368    *
369    * <p>
370    * Shortcut for calling <code>sortProperties(<jk>true</jk>)</code>.
371    *
372    * <h5 class='section'>See Also:</h5>
373    * <ul>
374    *    <li class='ja'>{@link Bean#sort()}
375    *    <li class='jf'>{@link BeanContext#BEAN_sortProperties}
376    * </ul>
377    *
378    * @return This object (for method chaining).
379    */
380   public BeanFilterBuilder<T> sortProperties() {
381      this.sortProperties = true;
382      return this;
383   }
384
385   /**
386    * Configuration property:  Find fluent setters.
387    *
388    * <p>
389    * When enabled, fluent setters are detected on beans.
390    *
391    * <p>
392    * Fluent setters must have the following attributes:
393    * <ul>
394    *    <li>Public.
395    *    <li>Not static.
396    *    <li>Take in one parameter.
397    *    <li>Return the bean itself.
398    * </ul>
399    *
400    * <h5 class='section'>Example:</h5>
401    * <p class='bcode w800'>
402    *    <jc>// Define our filter.</jc>
403    *    <jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
404    *       <jk>public</jk> MyFilter() {
405    *          fluentSetters();
406    *       }
407    *    }
408    * </p>
409    *
410    * <h5 class='section'>See Also:</h5>
411    * <ul>
412    *    <li class='ja'>{@link Bean#fluentSetters()}
413    *    <li class='jf'>{@link BeanContext#BEAN_fluentSetters}
414    * </ul>
415    *
416    * @param value
417    *    The new value for this property.
418    *    <br>The default is <jk>false</jk>.
419    * @return This object (for method chaining).
420    */
421   public BeanFilterBuilder<T> fluentSetters(boolean value) {
422      this.fluentSetters = value;
423      return this;
424   }
425
426   /**
427    * Configuration property:  Find fluent setters.
428    *
429    * <p>
430    * Shortcut for calling <code>fluentSetters(<jk>true</jk>)</code>.
431    *
432    * <h5 class='section'>See Also:</h5>
433    * <ul>
434    *    <li class='ja'>{@link Bean#fluentSetters()}
435    *    <li class='jf'>{@link BeanContext#BEAN_fluentSetters}
436    * </ul>
437    *
438    * @return This object (for method chaining).
439    */
440   public BeanFilterBuilder<T> fluentSetters() {
441      this.fluentSetters = true;
442      return this;
443   }
444
445   /**
446    * Configuration property:  Bean property namer
447    *
448    * <p>
449    * The class to use for calculating bean property names.
450    *
451    * <h5 class='section'>Example:</h5>
452    * <p class='bcode w800'>
453    *    <jc>// Define our filter.</jc>
454    *    <jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
455    *       <jk>public</jk> MyFilter() {
456    *          <jc>// Use Dashed-Lower-Case property names.</jc>
457    *          <jc>// (e.g. "foo-bar-url" instead of "fooBarURL")</jc>
458    *          propertyNamer(PropertyNamerDLC.<jk>class</jk>);
459    *       }
460    *    }
461    *
462    *    <jc>// Register it with a serializer or parser.</jc>
463    *    WriterSerializer s = JsonSerializer
464    *       .<jsm>create</jsm>()
465    *       .beanFilters(MyFilter.<jk>class</jk>)
466    *       .build();
467    *
468    *    <jc>// Properties names will be Dashed-Lower-Case.</jc>
469    *    String json = s.serialize(<jk>new</jk> MyBean());
470    * </p>
471    *
472    * <h5 class='section'>See Also:</h5>
473    * <ul>
474    *    <li class='ja'>{@link Bean#propertyNamer()}
475    *    <li class='jf'>{@link BeanContext#BEAN_propertyNamer}
476    *    <li class='jc'>{@link PropertyNamer}
477    * </ul>
478    *
479    * @param value
480    *    The new value for this setting.
481    *    <br>The default is {@link PropertyNamerDefault}.
482    * @return This object (for method chaining).
483    */
484   public BeanFilterBuilder<T> propertyNamer(Class<? extends PropertyNamer> value) {
485      this.propertyNamer = value;
486      return this;
487   }
488
489   /**
490    * Configuration property:  Bean dictionary.
491    *
492    * <p>
493    * Adds to the list of classes that make up the bean dictionary for this bean.
494    *
495    * <h5 class='section'>Example:</h5>
496    * <p class='bcode w800'>
497    *    <jc>// Define our filter.</jc>
498    *    <jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
499    *       <jk>public</jk> MyFilter() {
500    *          <jc>// Our bean contains generic collections of Foo and Bar objects.</jc>
501    *          beanDictionary(Foo.<jk>class</jk>, Bar.<jk>class</jk>);
502    *       }
503    *    }
504    *
505    *    <jc>// Register it with a parser.</jc>
506    *    ReaderParser p = JsonParser
507    *       .<jsm>create</jsm>()
508    *       .beanFilters(MyFilter.<jk>class</jk>)
509    *       .build();
510    *
511    *    <jc>// Instantiate our bean.</jc>
512    *    MyBean myBean = p.parse(json);
513    * </p>
514    *
515    * <h5 class='section'>See Also:</h5>
516    * <ul>
517    *    <li class='ja'>{@link Bean#beanDictionary()}
518    *    <li class='jf'>{@link BeanContext#BEAN_beanDictionary}
519    * </ul>
520    *
521    * @param values
522    *    The values to add to this property.
523    * @return This object (for method chaining).
524    */
525   public BeanFilterBuilder<T> beanDictionary(Class<?>...values) {
526      if (beanDictionary == null)
527         beanDictionary = new ArrayList<>(Arrays.asList(values));
528      else for (Class<?> cc : values)
529         beanDictionary.add(cc);
530      return this;
531   }
532
533   /**
534    * Configuration property:  Property filter.
535    *
536    * <p>
537    * The property filter to use for intercepting and altering getter and setter calls.
538    *
539    * <h5 class='section'>Example:</h5>
540    * <p class='bcode w800'>
541    *    <jc>// Define our filter.</jc>
542    *    <jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
543    *       <jk>public</jk> MyFilter() {
544    *          <jc>// Our bean contains generic collections of Foo and Bar objects.</jc>
545    *          propertyFilter(AddressPropertyFilter.<jk>class</jk>);
546    *       }
547    *    }
548    *
549    *    <jc>// Register it with a serializer or parser.</jc>
550    *    WriterSerializer s = JsonSerializer
551    *       .<jsm>create</jsm>()
552    *       .beanFilters(MyFilter.<jk>class</jk>)
553    *       .build();
554    * </p>
555    *
556    * <h5 class='section'>See Also:</h5>
557    * <ul>
558    *    <li class='ja'>{@link Bean#propertyFilter()}
559    *    <li class='jc'>{@link PropertyFilter}
560    * </ul>
561    *
562    * @param value
563    *    The new value for this setting.
564    *    <br>The default value is {@link PropertyFilter}.
565    * @return This object (for method chaining).
566    */
567   public BeanFilterBuilder<T> propertyFilter(Class<? extends PropertyFilter> value) {
568      this.propertyFilter = value;
569      return this;
570   }
571
572   /**
573    * Creates a {@link BeanFilter} with settings in this builder class.
574    *
575    * @return A new {@link BeanFilter} instance.
576    */
577   public BeanFilter build() {
578      return new BeanFilter(this);
579   }
580}