You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2020/10/15 17:56:36 UTC

[juneau] branch master updated: Simplify BeanConfig APIs

This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new a6b6859  Simplify BeanConfig APIs
a6b6859 is described below

commit a6b68594985c8e00fb03f4c3238190b4a8bd1bbd
Author: JamesBognar <ja...@salesforce.com>
AuthorDate: Thu Oct 15 13:56:30 2020 -0400

    Simplify BeanConfig APIs
---
 .../apache/juneau/BeanConfigAnnotationTest.java    |   4 +-
 .../java/org/apache/juneau/BeanConfigTest.java     |   6 +-
 .../main/java/org/apache/juneau/BeanContext.java   |  42 +-
 .../src/main/java/org/apache/juneau/BeanMap.java   |   2 +-
 .../src/main/java/org/apache/juneau/BeanMeta.java  |  12 +-
 .../main/java/org/apache/juneau/BeanSession.java   |   2 +-
 .../src/main/java/org/apache/juneau/ClassMeta.java |  12 +-
 .../org/apache/juneau/annotation/BeanConfig.java   |   4 +-
 .../transform/AnnotationBeanFilterBuilder.java     |  82 --
 .../org/apache/juneau/transform/BeanFilter.java    | 955 ++++++++++++++++-----
 .../apache/juneau/transform/BeanFilterBuilder.java | 672 ---------------
 .../transform/InterfaceBeanFilterBuilder.java      |  99 ---
 ...BeanFilter.java => UnmodifiableBeanFilter.java} |  10 +-
 .../apache/juneau/rest/jaxrs/JuneauProvider.java   |   4 +-
 14 files changed, 782 insertions(+), 1124 deletions(-)

diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/BeanConfigAnnotationTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/BeanConfigAnnotationTest.java
index 96998e7..f8d367c 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/BeanConfigAnnotationTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/BeanConfigAnnotationTest.java
@@ -64,8 +64,8 @@ public class BeanConfigAnnotationTest {
 				Map.Entry e = (Map.Entry)t;
 				return apply(e.getKey()) + "=" + apply(e.getValue());
 			}
-			if (t instanceof BeanFilter)
-				return ((BeanFilter)t).getBeanClass().getSimpleName();
+			if (t instanceof UnmodifiableBeanFilter)
+				return ((UnmodifiableBeanFilter)t).getBeanClass().getSimpleName();
 			if (t instanceof Class)
 				return ((Class<?>)t).getSimpleName();
 			if (t instanceof ClassInfo)
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/BeanConfigTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/BeanConfigTest.java
index 5207b6e..393c919 100755
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/BeanConfigTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/BeanConfigTest.java
@@ -770,9 +770,9 @@ public class BeanConfigTest {
 	public static class DummyPojoSwapA extends MapSwap<A> {}
 	public static class DummyPojoSwapB extends MapSwap<B> {}
 	public static class DummyPojoSwapC extends MapSwap<C> {}
-	public static class DummyBeanFilterA extends BeanFilterBuilder<A> {}
-	public static class DummyBeanFilterB extends BeanFilterBuilder<B> {}
-	public static class DummyBeanFilterC extends BeanFilterBuilder<C> {}
+	public static class DummyBeanFilterA extends BeanFilter<A> {}
+	public static class DummyBeanFilterB extends BeanFilter<B> {}
+	public static class DummyBeanFilterC extends BeanFilter<C> {}
 	public static class C {}
 
 	private void assertSameCache(ParserBuilder p1b, ParserBuilder p2b) {
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
index 59eae57..6afb857 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
@@ -40,7 +40,7 @@ import org.apache.juneau.utils.*;
  * 	<li>
  * 		Provides the ability to wrap beans inside {@link Map} interfaces.
  * 	<li>
- * 		Serves as a repository for metadata on POJOs, such as associated {@link BeanFilter BeanFilters},
+ * 		Serves as a repository for metadata on POJOs, such as associated {@link UnmodifiableBeanFilter BeanFilters},
  * 		{@link PropertyNamer PropertyNamers}, etc...  which are used to tailor how POJOs are serialized and parsed.
  * </ul>
  *
@@ -485,7 +485,7 @@ public class BeanContext extends Context implements MetaProvider {
 	 * 	<li><b>Methods:</b>
 	 * 		<ul>
 	 * 			<li class='jm'>{@link org.apache.juneau.BeanContextBuilder#dictionary(Object...)}
-	 * 			<li class='jm'>{@link org.apache.juneau.transform.BeanFilterBuilder#dictionary(Class...)}
+	 * 			<li class='jm'>{@link org.apache.juneau.transform.BeanFilter#dictionary(Class...)}
 	 * 		</ul>
 	 * </ul>
 	 *
@@ -1244,8 +1244,8 @@ public class BeanContext extends Context implements MetaProvider {
 	 * 	<li><b>Methods:</b>
 	 * 		<ul>
 	 * 			<li class='jm'>{@link org.apache.juneau.BeanContextBuilder#fluentSetters()}
-	 * 			<li class='jm'>{@link org.apache.juneau.transform.BeanFilterBuilder#fluentSetters(boolean)}
-	 * 			<li class='jm'>{@link org.apache.juneau.transform.BeanFilterBuilder#fluentSetters()}
+	 * 			<li class='jm'>{@link org.apache.juneau.transform.BeanFilter#fluentSetters(boolean)}
+	 * 			<li class='jm'>{@link org.apache.juneau.transform.BeanFilter#fluentSetters()}
 	 * 		</ul>
 	 * </ul>
 	 *
@@ -1853,7 +1853,7 @@ public class BeanContext extends Context implements MetaProvider {
 	 * 	<li><b>Methods:</b>
 	 * 		<ul>
 	 * 			<li class='jm'>{@link org.apache.juneau.BeanContextBuilder#propertyNamer(Class)}
-	 * 			<li class='jm'>{@link org.apache.juneau.transform.BeanFilterBuilder#propertyNamer(Class)}
+	 * 			<li class='jm'>{@link org.apache.juneau.transform.BeanFilter#propertyNamer(Class)}
 	 * 		</ul>
 	 * </ul>
 	 *
@@ -1916,8 +1916,8 @@ public class BeanContext extends Context implements MetaProvider {
 	 * 	<li><b>Methods:</b>
 	 * 		<ul>
 	 * 			<li class='jm'>{@link org.apache.juneau.BeanContextBuilder#sortProperties()}
-	 * 			<li class='jm'>{@link org.apache.juneau.transform.BeanFilterBuilder#sortProperties(boolean)}
-	 * 			<li class='jm'>{@link org.apache.juneau.transform.BeanFilterBuilder#sortProperties()}
+	 * 			<li class='jm'>{@link org.apache.juneau.transform.BeanFilter#sortProperties(boolean)}
+	 * 			<li class='jm'>{@link org.apache.juneau.transform.BeanFilter#sortProperties()}
 	 * 		</ul>
 	 * </ul>
 	 *
@@ -2317,7 +2317,7 @@ public class BeanContext extends Context implements MetaProvider {
 	private final Class<?>[] notBeanClasses;
 	private final List<Class<?>> beanDictionaryClasses;
 	private final String[] notBeanPackageNames, notBeanPackagePrefixes;
-	private final BeanFilter[] beanFilters;
+	private final UnmodifiableBeanFilter[] beanFilters;
 	private final PojoSwap<?,?>[] swaps;
 	private final Map<String,?> examples;
 	private final BeanRegistry beanRegistry;
@@ -2405,17 +2405,19 @@ public class BeanContext extends Context implements MetaProvider {
 		notBeanPackageNames = l1.toArray(new String[l1.size()]);
 		notBeanPackagePrefixes = l2.toArray(new String[l2.size()]);
 
-		LinkedList<BeanFilter> lbf = new LinkedList<>();
+		LinkedList<UnmodifiableBeanFilter> lbf = new LinkedList<>();
 		for (Object o : getListProperty(BEAN_beanFilters, Object[].class)) {
 			ClassInfo ci = o instanceof Class ? ClassInfo.of((Class)o) : ClassInfo.of(o);
-			if (ci.isChildOf(BeanFilter.class))
-				lbf.add(castOrCreate(BeanFilter.class, o));
-			else if (ci.isChildOf(BeanFilterBuilder.class))
-				lbf.add(castOrCreate(BeanFilterBuilder.class, o).build());
-			else if (o instanceof Class)
-				lbf.add(new InterfaceBeanFilterBuilder((Class<?>)o, this).build());
+			if (ci.isChildOf(UnmodifiableBeanFilter.class))
+				lbf.add(castOrCreate(UnmodifiableBeanFilter.class, o));
+			else if (ci.isChildOf(BeanFilter.class))
+				lbf.add(castOrCreate(BeanFilter.class, o).build());
+			else if (o instanceof Class) {
+				Class<?> ic = (Class<?>) o;
+				lbf.add(BeanFilter.create(ic).interfaceClass(ic).applyAnnotations(ClassInfo.of(ic).getAnnotations(Bean.class, this)).build());
+			}
 		}
-		beanFilters = lbf.toArray(new BeanFilter[0]);
+		beanFilters = lbf.toArray(new UnmodifiableBeanFilter[0]);
 
 		LinkedList<PojoSwap<?,?>> lpf = new LinkedList<>();
 		for (Object o : getListProperty(BEAN_swaps, Object.class)) {
@@ -3046,16 +3048,16 @@ public class BeanContext extends Context implements MetaProvider {
 	}
 
 	/**
-	 * Returns the {@link BeanFilter} associated with the specified class, or <jk>null</jk> if there is no bean filter
+	 * Returns the {@link UnmodifiableBeanFilter} associated with the specified class, or <jk>null</jk> if there is no bean filter
 	 * associated with the class.
 	 *
 	 * @param <T> The class associated with the bean filter.
 	 * @param c The class associated with the bean filter.
 	 * @return The bean filter associated with the class, or null if there is no association.
 	 */
-	private final <T> BeanFilter findBeanFilter(Class<T> c) {
+	private final <T> UnmodifiableBeanFilter findBeanFilter(Class<T> c) {
 		if (c != null)
-			for (BeanFilter f : beanFilters)
+			for (UnmodifiableBeanFilter f : beanFilters)
 				if (ClassInfo.of(f.getBeanClass()).isParentOf(c))
 					return f;
 		return null;
@@ -3642,7 +3644,7 @@ public class BeanContext extends Context implements MetaProvider {
 	 * @return
 	 * 	Only look for bean fields with this specified minimum visibility.
 	 */
-	protected final BeanFilter[] getBeanFilters() {
+	protected final UnmodifiableBeanFilter[] getBeanFilters() {
 		return beanFilters;
 	}
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java
index 40e8008..c273208 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java
@@ -51,7 +51,7 @@ import org.apache.juneau.xml.annotation.*;
  * </ul>
  *
  * <p>
- * <br>The order can also be overridden through the use of a {@link BeanFilter}.
+ * <br>The order can also be overridden through the use of a {@link UnmodifiableBeanFilter}.
  *
  * <h5 class='topic'>POJO swaps</h5>
  *
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
index 2e4d9a1..a9cdd93 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
@@ -35,7 +35,7 @@ import org.apache.juneau.transform.*;
  * <h5 class='topic'>Description</h5>
  *
  * Uses introspection to find all the properties associated with this class.  If the {@link Bean @Bean} annotation
- * 	is present on the class, or the class has a {@link BeanFilter} registered with it in the bean context,
+ * 	is present on the class, or the class has a {@link UnmodifiableBeanFilter} registered with it in the bean context,
  * 	then that information is used to determine the properties on the class.
  * Otherwise, the {@code BeanInfo} functionality in Java is used to determine the properties on the class.
  *
@@ -56,7 +56,7 @@ import org.apache.juneau.transform.*;
  * </ul>
  *
  * <p>
- * The order can also be overridden through the use of an {@link BeanFilter}.
+ * The order can also be overridden through the use of an {@link UnmodifiableBeanFilter}.
  *
  * @param <T> The class type that this metadata applies to.
  */
@@ -84,7 +84,7 @@ public class BeanMeta<T> {
 	protected final BeanContext ctx;
 
 	/** Optional bean filter associated with the target class. */
-	protected final BeanFilter beanFilter;
+	protected final UnmodifiableBeanFilter beanFilter;
 
 	/** Type variables implemented by this bean. */
 	protected final Map<Class<?>,Class<?>[]> typeVarImpls;
@@ -113,7 +113,7 @@ public class BeanMeta<T> {
 	 * @param beanFilter Optional bean filter associated with the target class.  Can be <jk>null</jk>.
 	 * @param pNames Explicit list of property names and order of properties.  If <jk>null</jk>, determine automatically.
 	 */
-	protected BeanMeta(final ClassMeta<T> classMeta, BeanContext ctx, BeanFilter beanFilter, String[] pNames) {
+	protected BeanMeta(final ClassMeta<T> classMeta, BeanContext ctx, UnmodifiableBeanFilter beanFilter, String[] pNames) {
 		this.classMeta = classMeta;
 		this.ctx = ctx;
 		this.c = classMeta.getInnerClass();
@@ -141,7 +141,7 @@ public class BeanMeta<T> {
 	private static final class Builder<T> {
 		ClassMeta<T> classMeta;
 		BeanContext ctx;
-		BeanFilter beanFilter;
+		UnmodifiableBeanFilter beanFilter;
 		String[] pNames;
 		Map<String,BeanPropertyMeta> properties;
 		AMap<String,BeanPropertyMeta> hiddenProperties = AMap.of();
@@ -157,7 +157,7 @@ public class BeanMeta<T> {
 		String dictionaryName, typePropertyName;
 		boolean sortProperties, fluentSetters;
 
-		Builder(ClassMeta<T> classMeta, BeanContext ctx, BeanFilter beanFilter, String[] pNames) {
+		Builder(ClassMeta<T> classMeta, BeanContext ctx, UnmodifiableBeanFilter beanFilter, String[] pNames) {
 			this.classMeta = classMeta;
 			this.ctx = ctx;
 			this.beanFilter = beanFilter;
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
index 3fbb500..0effc4a 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
@@ -1222,7 +1222,7 @@ public class BeanSession extends Session {
 	 * @return
 	 * 	Only look for bean fields with this specified minimum visibility.
 	 */
-	protected BeanFilter[] getBeanFilters() {
+	protected UnmodifiableBeanFilter[] getBeanFilters() {
 		return ctx.getBeanFilters();
 	}
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
index 3b2a814..025407d 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
@@ -93,7 +93,7 @@ public final class ClassMeta<T> implements Type {
 		childSwapMap,                                        // Maps normal subclasses to PojoSwaps.
 		childUnswapMap;                                      // Maps swap subclasses to PojoSwaps.
 	private final PojoSwap<T,?>[] swaps;                     // The object POJO swaps associated with this bean (if it has any).
-	private final BeanFilter beanFilter;                    // The bean filter associated with this bean (if it has one).
+	private final UnmodifiableBeanFilter beanFilter;                    // The bean filter associated with this bean (if it has one).
 	private final BuilderSwap<T,?> builderSwap;             // The builder swap associated with this bean (if it has one).
 	private final BeanContext beanContext;                  // The bean context that created this object.
 	private final ClassMeta<?>
@@ -126,7 +126,7 @@ public final class ClassMeta<T> implements Type {
 	 * 	For interfaces and abstract classes, this represents the "real" class to instantiate.
 	 * 	Can be <jk>null</jk>.
 	 * @param beanFilter
-	 * 	The {@link BeanFilter} programmatically associated with this class.
+	 * 	The {@link UnmodifiableBeanFilter} programmatically associated with this class.
 	 * 	Can be <jk>null</jk>.
 	 * @param pojoSwap
 	 * 	The {@link PojoSwap} programmatically associated with this class.
@@ -140,7 +140,7 @@ public final class ClassMeta<T> implements Type {
 	 * 	Used for delayed initialization when the possibility of class reference loops exist.
 	 */
 	@SuppressWarnings({ "rawtypes", "unchecked" })
-	ClassMeta(Class<T> innerClass, BeanContext beanContext, Class<? extends T> implClass, BeanFilter beanFilter, PojoSwap<T,?>[] swaps, PojoSwap<?,?>[] childPojoSwaps, Object example) {
+	ClassMeta(Class<T> innerClass, BeanContext beanContext, Class<? extends T> implClass, UnmodifiableBeanFilter beanFilter, PojoSwap<T,?>[] swaps, PojoSwap<?,?>[] childPojoSwaps, Object example) {
 		this.innerClass = innerClass;
 		this.info = ClassInfo.of(innerClass);
 		this.beanContext = beanContext;
@@ -350,7 +350,7 @@ public final class ClassMeta<T> implements Type {
 		Object example;
 		Mutater<String,T> stringMutater;
 
-		ClassMetaBuilder(Class<T> innerClass, BeanContext beanContext, Class<? extends T> implClass, BeanFilter beanFilter, PojoSwap<T,?>[] swaps, PojoSwap<?,?>[] childPojoSwaps, Object example) {
+		ClassMetaBuilder(Class<T> innerClass, BeanContext beanContext, Class<? extends T> implClass, UnmodifiableBeanFilter beanFilter, PojoSwap<T,?>[] swaps, PojoSwap<?,?>[] childPojoSwaps, Object example) {
 			this.innerClass = innerClass;
 			this.beanContext = beanContext;
 			BeanContext bc = beanContext;
@@ -684,11 +684,11 @@ public final class ClassMeta<T> implements Type {
 			this.stringMutater = Mutaters.get(String.class, c);
 		}
 
-		private BeanFilter findBeanFilter(BeanContext bc) {
+		private UnmodifiableBeanFilter findBeanFilter(BeanContext bc) {
 			try {
 				List<Bean> ba = info.getAnnotations(Bean.class, bc);
 				if (! ba.isEmpty())
-					return new AnnotationBeanFilterBuilder(innerClass, ba).build();
+					return BeanFilter.create(innerClass).applyAnnotations(ba).build();
 			} catch (Exception e) {
 				throw new RuntimeException(e);
 			}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/BeanConfig.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/BeanConfig.java
index a548a48..10e06d5 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/BeanConfig.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/BeanConfig.java
@@ -269,10 +269,10 @@ public @interface BeanConfig {
 	 * 	<li>
 	 * 		Values can consist of any of the following types:
 	 * 		<ul class='spaced-list'>
-	 * 			<li>Any subclass of {@link BeanFilterBuilder}.
+	 * 			<li>Any subclass of {@link BeanFilter}.
 	 * 				<br>These must have a public no-arg constructor.
 	 * 			<li>Any bean interfaces.
-	 * 				<br>A shortcut for defining a {@link InterfaceBeanFilterBuilder}.
+	 * 				<br>A shortcut for defining a {@link BeanFilter} with {@link BeanFilter#interfaceClass(Class)}.
 	 * 				<br>Any subclasses of an interface class will only have properties defined on the interface.
 	 * 				<br>All other bean properties will be ignored.
 	 * 		</ul>
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AnnotationBeanFilterBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AnnotationBeanFilterBuilder.java
deleted file mode 100644
index c3951eb..0000000
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AnnotationBeanFilterBuilder.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// ***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
-// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
-// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
-// * with the License.  You may obtain a copy of the License at                                                              *
-// *                                                                                                                         *
-// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
-// *                                                                                                                         *
-// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
-// * specific language governing permissions and limitations under the License.                                              *
-// ***************************************************************************************************************************
-package org.apache.juneau.transform;
-
-import static org.apache.juneau.internal.StringUtils.*;
-
-import java.util.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.annotation.*;
-
-/**
- * Bean filter builder initialized from the contents of a {@link Bean @Bean} annotation found on a class.
- *
- * <p>
- * <b>*** Internal class - Not intended for external use ***</b>
- *
- * @param <T> Annotated bean class.
- */
-public final class AnnotationBeanFilterBuilder<T> extends BeanFilterBuilder<T> {
-
-	/**
-	 * Constructor.
-	 *
-	 * @param annotatedClass The class found to have a {@link Bean @Bean} annotation.
-	 * @param annotations
-	 * 	The {@link Bean @Bean} annotations found on the class and all parent classes in child-to-parent order.
-	 * @throws Exception Thrown from property namer constructor.
-	 */
-	public AnnotationBeanFilterBuilder(Class<T> annotatedClass, List<Bean> annotations) throws Exception {
-		super(annotatedClass);
-
-		for (Bean b : annotations) {
-
-			if (! b.bpi().isEmpty())
-				bpi(split(b.bpi()));
-
-			if (! b.typeName().isEmpty())
-				typeName(b.typeName());
-
-			if (b.sort())
-				sortProperties(true);
-
-			if (b.fluentSetters())
-				fluentSetters(true);
-
-			if (! b.bpx().isEmpty())
-				bpx(split(b.bpx()));
-
-			if (! b.bpro().isEmpty())
-				bpro(split(b.bpro()));
-
-			if (! b.bpwo().isEmpty())
-				bpwo(split(b.bpwo()));
-
-			if (b.propertyNamer() != PropertyNamerDefault.class)
-				propertyNamer(b.propertyNamer());
-
-			if (b.interfaceClass() != Object.class)
-				interfaceClass(b.interfaceClass());
-
-			if (b.stopClass() != Object.class)
-				stopClass(b.stopClass());
-
-			if (b.dictionary().length > 0)
-				dictionary(b.dictionary());
-
-			if (b.interceptor() != BeanInterceptor.Default.class)
-				interceptor(b.interceptor());
-		}
-	}
-}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BeanFilter.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BeanFilter.java
index f2e0d50..52082ad 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BeanFilter.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BeanFilter.java
@@ -1,223 +1,732 @@
-// ***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
-// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
-// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
-// * with the License.  You may obtain a copy of the License at                                                              *
-// *                                                                                                                         *
-// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
-// *                                                                                                                         *
-// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
-// * specific language governing permissions and limitations under the License.                                              *
-// ***************************************************************************************************************************
-package org.apache.juneau.transform;
-
-import java.util.*;
-
-import static org.apache.juneau.internal.ClassUtils.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.annotation.*;
-
-/**
- * Parent class for all bean filters.
- *
- * <p>
- * Bean filters are used to control aspects of how beans are handled during serialization and parsing.
- *
- * <p>
- * Bean filters are created by {@link BeanFilterBuilder} which is the programmatic equivalent to the {@link Bean @Bean}
- * annotation.
- *
- * <ul class='seealso'>
- * 	<li class='link'>{@doc BeanFilters}
- * </ul>
- */
-public final class BeanFilter {
-
-	private final Class<?> beanClass;
-	private final Set<String> bpi, bpx, bpro, bpwo;
-	private final PropertyNamer propertyNamer;
-	private final Class<?> interfaceClass, stopClass;
-	private final boolean sortProperties, fluentSetters;
-	private final String typeName;
-	private final Class<?>[] beanDictionary;
-	@SuppressWarnings("rawtypes")
-	private final BeanInterceptor interceptor;
-
-	/**
-	 * Constructor.
-	 */
-	BeanFilter(BeanFilterBuilder<?> builder) {
-		this.beanClass = builder.beanClass;
-		this.typeName = builder.typeName;
-		this.bpi = new LinkedHashSet<>(builder.bpi);
-		this.bpx = new LinkedHashSet<>(builder.bpx);
-		this.bpro = new LinkedHashSet<>(builder.bpro);
-		this.bpwo = new LinkedHashSet<>(builder.bpwo);
-		this.interfaceClass = builder.interfaceClass;
-		this.stopClass = builder.stopClass;
-		this.sortProperties = builder.sortProperties;
-		this.fluentSetters = builder.fluentSetters;
-		this.propertyNamer = castOrCreate(PropertyNamer.class, builder.propertyNamer);
-		this.beanDictionary =
-			builder.dictionary == null
-			? null
-			: builder.dictionary.toArray(new Class<?>[builder.dictionary.size()]);
-		this.interceptor =
-			builder.interceptor == null
-			? BeanInterceptor.DEFAULT
-			: castOrCreate(BeanInterceptor.class, builder.interceptor);
-	}
-
-	/**
-	 * Static creator.
-	 *
-	 * @param <T> The class being filtered.
-	 * @param c The class being filtered.
-	 * @return A new instance of BeanFilterBuilder.
-	 */
-	public static <T> BeanFilterBuilder<T> create(Class<T> c) {
-		return new BeanFilterBuilder<>(c);
-	}
-
-	/**
-	 * Returns the bean class that this filter applies to.
-	 *
-	 * @return The bean class that this filter applies to.
-	 */
-	public Class<?> getBeanClass() {
-		return beanClass;
-	}
-
-	/**
-	 * Returns the dictionary name associated with this bean.
-	 *
-	 * @return The dictionary name associated with this bean, or <jk>null</jk> if no name is defined.
-	 */
-	public String getTypeName() {
-		return typeName;
-	}
-
-	/**
-	 * Returns the bean dictionary defined on this bean.
-	 *
-	 * @return The bean dictionary defined on this bean, or <jk>null</jk> if no bean dictionary is defined.
-	 */
-	public Class<?>[] getBeanDictionary() {
-		return beanDictionary;
-	}
-
-	/**
-	 * Returns the set and order of names of properties associated with a bean class.
-	 *
-	 * @return
-	 * 	The names of the properties associated with a bean class, or and empty set if all bean properties should
-	 * 	be used.
-	 */
-	public Set<String> getBpi() {
-		return bpi;
-	}
-
-	/**
-	 * Returns the list of properties to ignore on a bean.
-	 *
-	 * @return The names of the properties to ignore on a bean, or an empty set to not ignore any properties.
-	 */
-	public Set<String> getBpx() {
-		return bpx;
-	}
-
-	/**
-	 * Returns the list of read-only properties on a bean.
-	 *
-	 * @return The names of the read-only properties on a bean, or an empty set to not have any read-only properties.
-	 */
-	public Set<String> getBpro() {
-		return bpro;
-	}
-
-	/**
-	 * Returns the list of write-only properties on a bean.
-	 *
-	 * @return The names of the write-only properties on a bean, or an empty set to not have any write-only properties.
-	 */
-	public Set<String> getBpwo() {
-		return bpwo;
-	}
-
-	/**
-	 * Returns <jk>true</jk> if the properties defined on this bean class should be ordered alphabetically.
-	 *
-	 * <p>
-	 * This method is only used when the {@link #getBpi()} method returns <jk>null</jk>.
-	 * Otherwise, the ordering of the properties in the returned value is used.
-	 *
-	 * @return <jk>true</jk> if bean properties should be sorted.
-	 */
-	public boolean isSortProperties() {
-		return sortProperties;
-	}
-
-	/**
-	 * Returns <jk>true</jk> if we should find fluent setters.
-	 *
-	 * @return <jk>true</jk> if fluent setters should be found.
-	 */
-	public boolean isFluentSetters() {
-		return fluentSetters;
-	}
-
-	/**
-	 * Returns the {@link PropertyNamer} associated with the bean to tailor the names of bean properties.
-	 *
-	 * @return The property namer class, or <jk>null</jk> if no property namer is associated with this bean property.
-	 */
-	public PropertyNamer getPropertyNamer() {
-		return propertyNamer;
-	}
-
-	/**
-	 * Returns the interface class associated with this class.
-	 *
-	 * @return The interface class associated with this class, or <jk>null</jk> if no interface class is associated.
-	 */
-	public Class<?> getInterfaceClass() {
-		return interfaceClass;
-	}
-
-	/**
-	 * Returns the stop class associated with this class.
-	 *
-	 * @return The stop class associated with this class, or <jk>null</jk> if no stop class is associated.
-	 */
-	public Class<?> getStopClass() {
-		return stopClass;
-	}
-
-	/**
-	 * Calls the {@link BeanInterceptor#readProperty(Object, String, Object)} method on the registered property filters.
-	 *
-	 * @param bean The bean from which the property was read.
-	 * @param name The property name.
-	 * @param value The value just extracted from calling the bean getter.
-	 * @return The value to serialize.  Default is just to return the existing value.
-	 */
-	@SuppressWarnings("unchecked")
-	public Object readProperty(Object bean, String name, Object value) {
-		return interceptor.readProperty(bean, name, value);
-	}
-
-	/**
-	 * Calls the {@link BeanInterceptor#writeProperty(Object, String, Object)} method on the registered property filters.
-	 *
-	 * @param bean The bean from which the property was read.
-	 * @param name The property name.
-	 * @param value The value just parsed.
-	 * @return The value to serialize.  Default is just to return the existing value.
-	 */
-	@SuppressWarnings("unchecked")
-	public Object writeProperty(Object bean, String name, Object value) {
-		return interceptor.writeProperty(bean, name, value);
-	}
-}
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                                                              *
+// *                                                                                                                         *
+// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+// *                                                                                                                         *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the License.                                              *
+// ***************************************************************************************************************************
+package org.apache.juneau.transform;
+
+import static org.apache.juneau.internal.StringUtils.*;
+
+import java.beans.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.collections.*;
+import org.apache.juneau.reflect.*;
+
+/**
+ * Builder class for {@link UnmodifiableBeanFilter} objects.
+ *
+ * <p>
+ * This class is the programmatic equivalent to the {@link Bean @Bean} annotation.
+ *
+ * <p>
+ * The general approach to defining bean filters is to create subclasses from this class and call methods to
+ * set various attributes
+ * <p class='bcode w800'>
+ * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
+ *
+ * 		<jc>// Must provide a no-arg constructor!</jc>
+ * 		<jk>public</jk> MyFilter() {
+ *
+ * 			<jc>// Call one or more configuration methods.</jc>
+ * 			bpi(<js>"foo,bar,baz"</js>);
+ * 			sortProperties();
+ * 			propertyNamer(PropertyNamerULC.<jk>class</jk>);
+ * 		}
+ * 	}
+ *
+ * 	<jc>// Register it with a serializer or parser.</jc>
+ * 	WriterSerializer s = JsonSerializer
+ * 		.<jsm>create</jsm>()
+ * 		.beanFilters(MyFilter.<jk>class</jk>)
+ * 		.build();
+ * </p>
+ *
+ * <ul class='seealso'>
+ * 	<li class='link'>{@doc BeanFilters}
+ * </ul>
+ *
+ * @param <T> The bean type that this filter applies to.
+ */
+public class BeanFilter<T> {
+
+	Class<?> beanClass;
+	String typeName;
+	ASet<String>
+		bpi = ASet.of(),
+		bpx = ASet.of(),
+		bpro = ASet.of(),
+		bpwo = ASet.of();
+	Class<?> interfaceClass, stopClass;
+	boolean sortProperties, fluentSetters;
+	Object propertyNamer;
+	List<Class<?>> dictionary;
+	Object interceptor;
+
+	/**
+	 * Constructor.
+	 *
+	 * <p>
+	 * Bean class is determined through reflection of the parameter type.
+	 */
+	protected BeanFilter() {
+		beanClass = ClassInfo.of(this.getClass()).getParameterType(0, BeanFilter.class);
+	}
+
+	/**
+	 * Constructor.
+	 *
+	 * @param beanClass The bean class that this filter applies to.
+	 */
+	protected BeanFilter(Class<?> beanClass) {
+		this.beanClass = beanClass;
+	}
+
+	/**
+	 * Create a new instance of this bean filter.
+	 *
+	 * @param <T> The bean class being filtered.
+	 * @param beanClass The bean class being filtered.
+	 * @return A new {@link BeanFilter} object.
+	 */
+	public static <T> BeanFilter<T> create(Class<T> beanClass) {
+		return new BeanFilter<>(beanClass);
+	}
+
+	/**
+	 * Applies the information in the specified list of {@link Bean @Bean} annotations to this filter.
+	 *
+	 * @param annotations The annotations to apply.
+	 * @return This object (for method chaining).
+	 */
+	public BeanFilter<T> applyAnnotations(List<Bean> annotations) {
+
+		for (Bean b : annotations) {
+
+			if (! b.bpi().isEmpty())
+				bpi(split(b.bpi()));
+
+			if (! b.typeName().isEmpty())
+				typeName(b.typeName());
+
+			if (b.sort())
+				sortProperties(true);
+
+			if (b.fluentSetters())
+				fluentSetters(true);
+
+			if (! b.bpx().isEmpty())
+				bpx(split(b.bpx()));
+
+			if (! b.bpro().isEmpty())
+				bpro(split(b.bpro()));
+
+			if (! b.bpwo().isEmpty())
+				bpwo(split(b.bpwo()));
+
+			if (b.propertyNamer() != PropertyNamerDefault.class)
+				propertyNamer(b.propertyNamer());
+
+			if (b.interfaceClass() != Object.class)
+				interfaceClass(b.interfaceClass());
+
+			if (b.stopClass() != Object.class)
+				stopClass(b.stopClass());
+
+			if (b.dictionary().length > 0)
+				dictionary(b.dictionary());
+
+			if (b.interceptor() != BeanInterceptor.Default.class)
+				interceptor(b.interceptor());
+		}
+		return this;
+	}
+
+	/**
+	 * Configuration property:  Bean dictionary type name.
+	 *
+	 * <p>
+	 * Specifies the dictionary type name for this bean.
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Define our filter.</jc>
+	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
+	 * 		<jk>public</jk> MyFilter() {
+	 * 			typeName(<js>"mybean"</js>);
+	 * 		}
+	 * 	}
+	 *
+	 * 	<jc>// Register it with a serializer or parser.</jc>
+	 * 	WriterSerializer s = JsonSerializer
+	 * 		.<jsm>create</jsm>()
+	 * 		.beanFilters(MyFilter.<jk>class</jk>)
+	 * 		.build();
+	 *
+	 * 	<jc>// Produces:  "{_type:'mybean', ...}"</jc>
+	 * 	String json = s.serialize(<jk>new</jk> MyBean());
+	 * </p>
+	 *
+	 * <ul class='seealso'>
+	 * 	<li class='ja'>{@link Bean#typeName()}
+	 * </ul>
+	 *
+	 * @param value The new value for this setting.
+	 * @return This object (for method chaining).
+	 */
+	public BeanFilter<T> typeName(String value) {
+		this.typeName = value;
+		return this;
+	}
+
+	/**
+	 * Configuration property:  Bean interface class.
+	 *
+	 * Identifies a class to be used as the interface class for this and all subclasses.
+	 *
+	 * <p>
+	 * When specified, only the list of properties defined on the interface class will be used during serialization.
+	 * <br>Additional properties on subclasses will be ignored.
+	 *
+	 * <p class='bcode w800'>
+	 * 	<jc>// Parent class</jc>
+	 * 	<jk>public abstract class</jk> A {
+	 * 		<jk>public</jk> String <jf>f0</jf> = <js>"f0"</js>;
+	 * 	}
+	 *
+	 * 	<jc>// Sub class</jc>
+	 * 	<jk>public class</jk> A1 <jk>extends</jk> A {
+	 * 		<jk>public</jk> String <jf>f1</jf> = <js>"f1"</js>;
+	 * 	}
+	 *
+	 * 	<jc>// Define our filter.</jc>
+	 * 	<jk>public class</jk> AFilter <jk>extends</jk> BeanFilterBuilder&lt;A&gt; {
+	 * 		<jk>public</jk> AFilter() {
+	 * 			interfaceClass(A.<jk>class</jk>);
+	 * 		}
+	 * 	}
+	 *
+	 * 	<jc>// Register it with a serializer.</jc>
+	 * 	WriterSerializer s = JsonSerializer
+	 * 		.<jsm>create</jsm>()
+	 * 		.beanFilters(AFilter.<jk>class</jk>)
+	 * 		.build();
+	 *
+	 * 	<jc>// Use it.</jc>
+	 * 	A1 a1 = <jk>new</jk> A1();
+	 * 	String r = s.serialize(a1);
+	 * 	<jsm>assertEquals</jsm>(<js>"{f0:'f0'}"</js>, r);  <jc>// Note f1 is not serialized</jc>
+	 * </p>
+	 *
+	 * <p>
+	 * Note that this filter can be used on the parent class so that it filters to all child classes, or can be set
+	 * individually on the child classes.
+	 *
+	 * <ul class='seealso'>
+	 * 	<li class='ja'>{@link Bean#interfaceClass()}
+	 * </ul>
+	 *
+	 * @param value The new value for this setting.
+	 * @return This object (for method chaining).
+	 */
+	public BeanFilter<T> interfaceClass(Class<?> value) {
+		this.interfaceClass = value;
+		return this;
+	}
+
+	/**
+	 * Configuration property:  Bean stop class.
+	 *
+	 * <p>
+	 * Identifies a stop class for this class and all subclasses.
+	 *
+	 * <p>
+	 * Identical in purpose to the stop class specified by {@link Introspector#getBeanInfo(Class, Class)}.
+	 * <br>Any properties in the stop class or in its base classes will be ignored during analysis.
+	 *
+	 * <p>
+	 * For example, in the following class hierarchy, instances of <c>C3</c> will include property <c>p3</c>,
+	 * but not <c>p1</c> or <c>p2</c>.
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jk>public class</jk> C1 {
+	 * 		<jk>public int</jk> getP1();
+	 * 	}
+	 *
+	 * 	<jk>public class</jk> C2 <jk>extends</jk> C1 {
+	 * 		<jk>public int</jk> getP2();
+	 * 	}
+	 *
+	 * 	<jk>public class</jk> C3 <jk>extends</jk> C2 {
+	 * 		<jk>public int</jk> getP3();
+	 * 	}
+	 *
+	 * 	<jc>// Define our filter.</jc>
+	 * 	<jk>public class</jk> C3Filter <jk>extends</jk> BeanFilterBuilder&lt;C3&gt; {
+	 * 		<jk>public</jk> C3Filter() {
+	 * 			stopClass(C2.<jk>class</jk>);
+	 * 		}
+	 * 	}
+	 *
+	 * 	<jc>// Register it with a serializer.</jc>
+	 * 	WriterSerializer s = JsonSerializer
+	 * 		.<jsm>create</jsm>()
+	 * 		.beanFilters(C3Filter.<jk>class</jk>)
+	 * 		.build();
+	 *
+	 * 	<jc>// Serializes property 'p3', but NOT 'p1' or 'p2'.</jc>
+	 * 	String json = s.serialize(<jk>new</jk> C3());
+	 * </p>
+	 *
+	 * <ul class='seealso'>
+	 * 	<li class='ja'>{@link Bean#stopClass()}
+	 * </ul>
+	 *
+	 * @param value The new value for this setting.
+	 * @return This object (for method chaining).
+	 */
+	public BeanFilter<T> stopClass(Class<?> value) {
+		this.stopClass = value;
+		return this;
+	}
+
+	/**
+	 * Configuration property:  Sort bean properties.
+	 *
+	 * <p>
+	 * When <jk>true</jk>, all bean properties will be serialized and access in alphabetical order.
+	 * <br>Otherwise, the natural order of the bean properties is used which is dependent on the JVM vendor.
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Define our filter.</jc>
+	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
+	 * 		<jk>public</jk> MyFilter() {
+	 * 			sortProperties();
+	 * 		}
+	 * 	}
+	 *
+	 * 	<jc>// Register it with a serializer.</jc>
+	 * 	WriterSerializer s = JsonSerializer
+	 * 		.<jsm>create</jsm>()
+	 * 		.beanFilters(MyFilter.<jk>class</jk>)
+	 * 		.build();
+	 *
+	 * 	<jc>// Properties will be sorted alphabetically.</jc>
+	 * 	String json = s.serialize(<jk>new</jk> MyBean());
+	 * </p>
+	 *
+	 * <ul class='seealso'>
+	 * 	<li class='ja'>{@link Bean#sort()}
+	 * 	<li class='jf'>{@link BeanContext#BEAN_sortProperties}
+	 * </ul>
+	 *
+	 * @param value
+	 * 	The new value for this property.
+	 * 	<br>The default is <jk>false</jk>.
+	 * @return This object (for method chaining).
+	 */
+	public BeanFilter<T> sortProperties(boolean value) {
+		this.sortProperties = value;
+		return this;
+	}
+
+	/**
+	 * Configuration property:  Sort bean properties.
+	 *
+	 * <p>
+	 * Shortcut for calling <code>sortProperties(<jk>true</jk>)</code>.
+	 *
+	 * <ul class='seealso'>
+	 * 	<li class='ja'>{@link Bean#sort()}
+	 * 	<li class='jf'>{@link BeanContext#BEAN_sortProperties}
+	 * </ul>
+	 *
+	 * @return This object (for method chaining).
+	 */
+	public BeanFilter<T> sortProperties() {
+		this.sortProperties = true;
+		return this;
+	}
+
+	/**
+	 * Configuration property:  Find fluent setters.
+	 *
+	 * <p>
+	 * When enabled, fluent setters are detected on beans.
+	 *
+	 * <p>
+	 * Fluent setters must have the following attributes:
+	 * <ul>
+	 * 	<li>Public.
+	 * 	<li>Not static.
+	 * 	<li>Take in one parameter.
+	 * 	<li>Return the bean itself.
+	 * </ul>
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Define our filter.</jc>
+	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
+	 * 		<jk>public</jk> MyFilter() {
+	 * 			fluentSetters();
+	 * 		}
+	 * 	}
+	 * </p>
+	 *
+	 * <ul class='seealso'>
+	 * 	<li class='ja'>{@link Bean#fluentSetters()}
+	 * 	<li class='jf'>{@link BeanContext#BEAN_fluentSetters}
+	 * </ul>
+	 *
+	 * @param value
+	 * 	The new value for this property.
+	 * 	<br>The default is <jk>false</jk>.
+	 * @return This object (for method chaining).
+	 */
+	public BeanFilter<T> fluentSetters(boolean value) {
+		this.fluentSetters = value;
+		return this;
+	}
+
+	/**
+	 * Configuration property:  Find fluent setters.
+	 *
+	 * <p>
+	 * Shortcut for calling <code>fluentSetters(<jk>true</jk>)</code>.
+	 *
+	 * <ul class='seealso'>
+	 * 	<li class='ja'>{@link Bean#fluentSetters()}
+	 * 	<li class='jf'>{@link BeanContext#BEAN_fluentSetters}
+	 * </ul>
+	 *
+	 * @return This object (for method chaining).
+	 */
+	public BeanFilter<T> fluentSetters() {
+		this.fluentSetters = true;
+		return this;
+	}
+
+	/**
+	 * Configuration property:  Bean property namer
+	 *
+	 * <p>
+	 * The class to use for calculating bean property names.
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Define our filter.</jc>
+	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
+	 * 		<jk>public</jk> MyFilter() {
+	 * 			<jc>// Use Dashed-Lower-Case property names.</jc>
+	 * 			<jc>// (e.g. "foo-bar-url" instead of "fooBarURL")</jc>
+	 * 			propertyNamer(PropertyNamerDLC.<jk>class</jk>);
+	 * 		}
+	 * 	}
+	 *
+	 * 	<jc>// Register it with a serializer or parser.</jc>
+	 * 	WriterSerializer s = JsonSerializer
+	 * 		.<jsm>create</jsm>()
+	 * 		.beanFilters(MyFilter.<jk>class</jk>)
+	 * 		.build();
+	 *
+	 * 	<jc>// Properties names will be Dashed-Lower-Case.</jc>
+	 * 	String json = s.serialize(<jk>new</jk> MyBean());
+	 * </p>
+	 *
+	 * <ul class='seealso'>
+	 * 	<li class='ja'>{@link Bean#propertyNamer()}
+	 * 	<li class='jf'>{@link BeanContext#BEAN_propertyNamer}
+	 * 	<li class='jc'>{@link PropertyNamer}
+	 * </ul>
+	 *
+	 * @param value
+	 * 	The new value for this setting.
+	 * 	<br>The default is {@link PropertyNamerDefault}.
+	 * @return This object (for method chaining).
+	 */
+	public BeanFilter<T> propertyNamer(Class<? extends PropertyNamer> value) {
+		this.propertyNamer = value;
+		return this;
+	}
+
+	/**
+	 * Configuration property:  Bean property includes.
+	 *
+	 * <p>
+	 * Specifies the set and order of names of properties associated with the bean class.
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Define our filter.</jc>
+	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
+	 * 		<jk>public</jk> MyFilter() {
+	 * 			bpi(<js>"foo,bar,baz"</js>);
+	 * 		}
+	 * 	}
+	 *
+	 * 	<jc>// Register it with a serializer.</jc>
+	 * 	WriterSerializer s = JsonSerializer
+	 * 		.<jsm>create</jsm>()
+	 * 		.beanFilters(MyFilter.<jk>class</jk>)
+	 * 		.build();
+	 *
+	 * 	<jc>// Only serializes the properties 'foo', 'bar', and 'baz'.</jc>
+	 * 	String json = s.serialize(<jk>new</jk> MyBean());
+	 * </p>
+	 *
+	 * <ul class='seealso'>
+	 * 	<li class='ja'>{@link Bean#bpi()}
+	 * 	<li class='jm'>{@link BeanContextBuilder#bpi(Class, String)}
+	 * 	<li class='jm'>{@link BeanContextBuilder#bpi(String, String)}
+	 * 	<li class='jm'>{@link BeanContextBuilder#bpi(Map)}
+	 * </ul>
+	 *
+	 * @param value
+	 * 	The new value for this setting.
+	 * 	<br>Values can contain comma-delimited list of property names.
+	 * @return This object (for method chaining).
+	 */
+	public BeanFilter<T> bpi(String...value) {
+		this.bpi = ASet.of();
+		for (String v : value)
+			bpi.a(split(v));
+		return this;
+	}
+
+	/**
+	 * Configuration property:  Bean property excludes.
+	 *
+	 * <p>
+	 * Specifies properties to exclude from the bean class.
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Define our filter.</jc>
+	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
+	 * 		<jk>public</jk> MyFilter() {
+	 * 			bpx(<js>"foo,bar"</js>);
+	 * 		}
+	 * 	}
+	 *
+	 * 	<jc>// Register it with a serializer.</jc>
+	 * 	WriterSerializer s = JsonSerializer
+	 * 		.<jsm>create</jsm>()
+	 * 		.beanFilters(MyFilter.<jk>class</jk>)
+	 * 		.build();
+	 *
+	 * 	<jc>// Serializes all properties except for 'foo' and 'bar'.</jc>
+	 * 	String json = s.serialize(<jk>new</jk> MyBean());
+	 * </p>
+	 *
+	 * <ul class='seealso'>
+	 * 	<li class='ja'>{@link Bean#bpx()}
+	 * 	<li class='jm'>{@link BeanContextBuilder#bpx(Class, String)}
+	 * 	<li class='jm'>{@link BeanContextBuilder#bpx(String, String)}
+	 * 	<li class='jm'>{@link BeanContextBuilder#bpx(Map)}
+	 * </ul>
+	 *
+	 * @param value
+	 * 	The new value for this setting.
+	 * 	<br>Values can contain comma-delimited list of property names.
+	 * @return This object (for method chaining).
+	 */
+	public BeanFilter<T> bpx(String...value) {
+		this.bpx = ASet.of();
+		for (String v : value)
+			bpx.a(split(v));
+		return this;
+	}
+
+	/**
+	 * Configuration property:  Read-only bean properties.
+	 *
+	 * <p>
+	 * Specifies one or more properties on a bean that are read-only despite having valid getters.
+	 * Serializers will serialize such properties as usual, but parsers will silently ignore them.
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Define our filter.</jc>
+	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
+	 * 		<jk>public</jk> MyFilter() {
+	 * 			bpro(<js>"foo,bar"</js>);
+	 * 		}
+	 * 	}
+	 *
+	 * 	<jc>// Register it with a parser.</jc>
+	 *  ReaderParser p = JsonParser
+	 * 		.<jsm>create</jsm>()
+	 * 		.beanFilters(MyFilter.<jk>class</jk>)
+	 * 		.build();
+	 *
+	 * 	<jc>// Parsers all properties except for 'foo' and 'bar'.</jc>
+	 * 	MyBean b = p.parse(<js>"..."</js>, MyBean.<jk>class</jk>);
+	 * </p>
+	 *
+	 * <ul class='seealso'>
+	 * 	<li class='ja'>{@link Bean#bpro()}
+	 * 	<li class='ja'>{@link Beanp#ro()}
+	 * 	<li class='jm'>{@link BeanContextBuilder#bpro(Class, String)}
+	 * 	<li class='jm'>{@link BeanContextBuilder#bpro(String, String)}
+	 * 	<li class='jm'>{@link BeanContextBuilder#bpro(Map)}
+	 * </ul>
+	 *
+	 * @param value
+	 * 	The new value for this setting.
+	 * 	<br>Values can contain comma-delimited list of property names.
+	 * @return This object (for method chaining).
+	 */
+	public BeanFilter<T> bpro(String...value) {
+		this.bpro = ASet.of();
+		for (String v : value)
+			bpro.a(split(v));
+		return this;
+	}
+
+	/**
+	 * Configuration property:  Write-only bean properties.
+	 *
+	 * <p>
+	 * Specifies one or more properties on a bean that are write-only despite having valid setters.
+	 * Parsers will parse such properties as usual, but serializers will silently ignore them.
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Define our filter.</jc>
+	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
+	 * 		<jk>public</jk> MyFilter() {
+	 * 			bpwo(<js>"foo,bar"</js>);
+	 * 		}
+	 * 	}
+	 *
+	 * 	<jc>// Register it with a serializer.</jc>
+	 *  WriterSerializer s = JsonSerializer
+	 * 		.<jsm>create</jsm>()
+	 * 		.beanFilters(MyFilter.<jk>class</jk>)
+	 * 		.build();
+	 *
+	 * 	<jc>// Serializes all properties except for 'foo' and 'bar'.</jc>
+	 * 	String json = s.serialize(<jk>new</jk> MyBean());
+	 * </p>
+	 *
+	 * <ul class='seealso'>
+	 * 	<li class='ja'>{@link Bean#bpwo()}
+	 * 	<li class='ja'>{@link Beanp#wo()}
+	 * 	<li class='jm'>{@link BeanContextBuilder#bpwo(Class, String)}
+	 * 	<li class='jm'>{@link BeanContextBuilder#bpwo(String, String)}
+	 * 	<li class='jm'>{@link BeanContextBuilder#bpwo(Map)}
+	 * </ul>
+	 *
+	 * @param value
+	 * 	The new value for this setting.
+	 * 	<br>Values can contain comma-delimited list of property names.
+	 * @return This object (for method chaining).
+	 */
+	public BeanFilter<T> bpwo(String...value) {
+		this.bpwo = ASet.of();
+		for (String v : value)
+			bpwo.a(split(v));
+		return this;
+	}
+
+	/**
+	 * Configuration property:  Bean dictionary.
+	 *
+	 * <p>
+	 * Adds to the list of classes that make up the bean dictionary for this bean.
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Define our filter.</jc>
+	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
+	 * 		<jk>public</jk> MyFilter() {
+	 * 			<jc>// Our bean contains generic collections of Foo and Bar objects.</jc>
+	 * 			beanDictionary(Foo.<jk>class</jk>, Bar.<jk>class</jk>);
+	 * 		}
+	 * 	}
+	 *
+	 * 	<jc>// Register it with a parser.</jc>
+	 * 	ReaderParser p = JsonParser
+	 * 		.<jsm>create</jsm>()
+	 * 		.beanFilters(MyFilter.<jk>class</jk>)
+	 * 		.build();
+	 *
+	 * 	<jc>// Instantiate our bean.</jc>
+	 * 	MyBean myBean = p.parse(json);
+	 * </p>
+	 *
+	 * <ul class='seealso'>
+	 * 	<li class='ja'>{@link Bean#dictionary()}
+	 * 	<li class='jf'>{@link BeanContext#BEAN_beanDictionary}
+	 * </ul>
+	 *
+	 * @param values
+	 * 	The values to add to this property.
+	 * @return This object (for method chaining).
+	 */
+	public BeanFilter<T> dictionary(Class<?>...values) {
+		if (dictionary == null)
+			dictionary = Arrays.asList(values);
+		else for (Class<?> cc : values)
+			dictionary.add(cc);
+		return this;
+	}
+
+	/**
+	 * Configuration property:  Bean interceptor.
+	 *
+	 * <p>
+	 * The interceptor to use for intercepting and altering getter and setter calls.
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Define our filter.</jc>
+	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
+	 * 		<jk>public</jk> MyFilter() {
+	 * 			<jc>// Our bean contains generic collections of Foo and Bar objects.</jc>
+	 * 			interceptor(AddressInterceptor.<jk>class</jk>);
+	 * 		}
+	 * 	}
+	 *
+	 * 	<jc>// Register it with a serializer or parser.</jc>
+	 * 	WriterSerializer s = JsonSerializer
+	 * 		.<jsm>create</jsm>()
+	 * 		.beanFilters(MyFilter.<jk>class</jk>)
+	 * 		.build();
+	 * </p>
+	 *
+	 * <ul class='seealso'>
+	 * 	<li class='ja'>{@link Bean#interceptor()}
+	 * 	<li class='jc'>{@link BeanInterceptor}
+	 * </ul>
+	 *
+	 * @param value
+	 * 	The new value for this setting.
+	 * 	<br>The default value is {@link BeanInterceptor}.
+	 * @return This object (for method chaining).
+	 */
+	public BeanFilter<T> interceptor(Class<?> value) {
+		this.interceptor = value;
+		return this;
+	}
+
+	/**
+	 * Creates a {@link UnmodifiableBeanFilter} with settings in this builder class.
+	 *
+	 * @return A new {@link UnmodifiableBeanFilter} instance.
+	 */
+	public UnmodifiableBeanFilter build() {
+		return new UnmodifiableBeanFilter(this);
+	}
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BeanFilterBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BeanFilterBuilder.java
deleted file mode 100644
index f78cd50..0000000
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BeanFilterBuilder.java
+++ /dev/null
@@ -1,672 +0,0 @@
-// ***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
-// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
-// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
-// * with the License.  You may obtain a copy of the License at                                                              *
-// *                                                                                                                         *
-// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
-// *                                                                                                                         *
-// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
-// * specific language governing permissions and limitations under the License.                                              *
-// ***************************************************************************************************************************
-package org.apache.juneau.transform;
-
-import static org.apache.juneau.internal.StringUtils.*;
-
-import java.beans.*;
-import java.util.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.annotation.*;
-import org.apache.juneau.collections.*;
-import org.apache.juneau.reflect.*;
-
-/**
- * Builder class for {@link BeanFilter} objects.
- *
- * <p>
- * This class is the programmatic equivalent to the {@link Bean @Bean} annotation.
- *
- * <p>
- * The general approach to defining bean filters is to create subclasses from this class and call methods to
- * set various attributes
- * <p class='bcode w800'>
- * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
- *
- * 		<jc>// Must provide a no-arg constructor!</jc>
- * 		<jk>public</jk> MyFilter() {
- *
- * 			<jc>// Call one or more configuration methods.</jc>
- * 			bpi(<js>"foo,bar,baz"</js>);
- * 			sortProperties();
- * 			propertyNamer(PropertyNamerULC.<jk>class</jk>);
- * 		}
- * 	}
- *
- * 	<jc>// Register it with a serializer or parser.</jc>
- * 	WriterSerializer s = JsonSerializer
- * 		.<jsm>create</jsm>()
- * 		.beanFilters(MyFilter.<jk>class</jk>)
- * 		.build();
- * </p>
- *
- * <ul class='seealso'>
- * 	<li class='link'>{@doc BeanFilters}
- * </ul>
- *
- * @param <T> The bean type that this filter applies to.
- */
-public class BeanFilterBuilder<T> {
-
-	Class<?> beanClass;
-	String typeName;
-	ASet<String>
-		bpi = ASet.of(),
-		bpx = ASet.of(),
-		bpro = ASet.of(),
-		bpwo = ASet.of();
-	Class<?> interfaceClass, stopClass;
-	boolean sortProperties, fluentSetters;
-	Object propertyNamer;
-	List<Class<?>> dictionary;
-	Object interceptor;
-
-	/**
-	 * Constructor.
-	 *
-	 * <p>
-	 * Bean class is determined through reflection of the parameter type.
-	 */
-	protected BeanFilterBuilder() {
-		beanClass = ClassInfo.of(this.getClass()).getParameterType(0, BeanFilterBuilder.class);
-	}
-
-	/**
-	 * Constructor.
-	 *
-	 * @param beanClass The bean class that this filter applies to.
-	 */
-	protected BeanFilterBuilder(Class<?> beanClass) {
-		this.beanClass = beanClass;
-	}
-
-	/**
-	 * Configuration property:  Bean dictionary type name.
-	 *
-	 * <p>
-	 * Specifies the dictionary type name for this bean.
-	 *
-	 * <h5 class='section'>Example:</h5>
-	 * <p class='bcode w800'>
-	 * 	<jc>// Define our filter.</jc>
-	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
-	 * 		<jk>public</jk> MyFilter() {
-	 * 			typeName(<js>"mybean"</js>);
-	 * 		}
-	 * 	}
-	 *
-	 * 	<jc>// Register it with a serializer or parser.</jc>
-	 * 	WriterSerializer s = JsonSerializer
-	 * 		.<jsm>create</jsm>()
-	 * 		.beanFilters(MyFilter.<jk>class</jk>)
-	 * 		.build();
-	 *
-	 * 	<jc>// Produces:  "{_type:'mybean', ...}"</jc>
-	 * 	String json = s.serialize(<jk>new</jk> MyBean());
-	 * </p>
-	 *
-	 * <ul class='seealso'>
-	 * 	<li class='ja'>{@link Bean#typeName()}
-	 * </ul>
-	 *
-	 * @param value The new value for this setting.
-	 * @return This object (for method chaining).
-	 */
-	public BeanFilterBuilder<T> typeName(String value) {
-		this.typeName = value;
-		return this;
-	}
-
-	/**
-	 * Configuration property:  Bean interface class.
-	 *
-	 * Identifies a class to be used as the interface class for this and all subclasses.
-	 *
-	 * <p>
-	 * When specified, only the list of properties defined on the interface class will be used during serialization.
-	 * <br>Additional properties on subclasses will be ignored.
-	 *
-	 * <p class='bcode w800'>
-	 * 	<jc>// Parent class</jc>
-	 * 	<jk>public abstract class</jk> A {
-	 * 		<jk>public</jk> String <jf>f0</jf> = <js>"f0"</js>;
-	 * 	}
-	 *
-	 * 	<jc>// Sub class</jc>
-	 * 	<jk>public class</jk> A1 <jk>extends</jk> A {
-	 * 		<jk>public</jk> String <jf>f1</jf> = <js>"f1"</js>;
-	 * 	}
-	 *
-	 * 	<jc>// Define our filter.</jc>
-	 * 	<jk>public class</jk> AFilter <jk>extends</jk> BeanFilterBuilder&lt;A&gt; {
-	 * 		<jk>public</jk> AFilter() {
-	 * 			interfaceClass(A.<jk>class</jk>);
-	 * 		}
-	 * 	}
-	 *
-	 * 	<jc>// Register it with a serializer.</jc>
-	 * 	WriterSerializer s = JsonSerializer
-	 * 		.<jsm>create</jsm>()
-	 * 		.beanFilters(AFilter.<jk>class</jk>)
-	 * 		.build();
-	 *
-	 * 	<jc>// Use it.</jc>
-	 * 	A1 a1 = <jk>new</jk> A1();
-	 * 	String r = s.serialize(a1);
-	 * 	<jsm>assertEquals</jsm>(<js>"{f0:'f0'}"</js>, r);  <jc>// Note f1 is not serialized</jc>
-	 * </p>
-	 *
-	 * <p>
-	 * Note that this filter can be used on the parent class so that it filters to all child classes, or can be set
-	 * individually on the child classes.
-	 *
-	 * <ul class='seealso'>
-	 * 	<li class='ja'>{@link Bean#interfaceClass()}
-	 * </ul>
-	 *
-	 * @param value The new value for this setting.
-	 * @return This object (for method chaining).
-	 */
-	public BeanFilterBuilder<T> interfaceClass(Class<?> value) {
-		this.interfaceClass = value;
-		return this;
-	}
-
-	/**
-	 * Configuration property:  Bean stop class.
-	 *
-	 * <p>
-	 * Identifies a stop class for this class and all subclasses.
-	 *
-	 * <p>
-	 * Identical in purpose to the stop class specified by {@link Introspector#getBeanInfo(Class, Class)}.
-	 * <br>Any properties in the stop class or in its base classes will be ignored during analysis.
-	 *
-	 * <p>
-	 * For example, in the following class hierarchy, instances of <c>C3</c> will include property <c>p3</c>,
-	 * but not <c>p1</c> or <c>p2</c>.
-	 *
-	 * <h5 class='section'>Example:</h5>
-	 * <p class='bcode w800'>
-	 * 	<jk>public class</jk> C1 {
-	 * 		<jk>public int</jk> getP1();
-	 * 	}
-	 *
-	 * 	<jk>public class</jk> C2 <jk>extends</jk> C1 {
-	 * 		<jk>public int</jk> getP2();
-	 * 	}
-	 *
-	 * 	<jk>public class</jk> C3 <jk>extends</jk> C2 {
-	 * 		<jk>public int</jk> getP3();
-	 * 	}
-	 *
-	 * 	<jc>// Define our filter.</jc>
-	 * 	<jk>public class</jk> C3Filter <jk>extends</jk> BeanFilterBuilder&lt;C3&gt; {
-	 * 		<jk>public</jk> C3Filter() {
-	 * 			stopClass(C2.<jk>class</jk>);
-	 * 		}
-	 * 	}
-	 *
-	 * 	<jc>// Register it with a serializer.</jc>
-	 * 	WriterSerializer s = JsonSerializer
-	 * 		.<jsm>create</jsm>()
-	 * 		.beanFilters(C3Filter.<jk>class</jk>)
-	 * 		.build();
-	 *
-	 * 	<jc>// Serializes property 'p3', but NOT 'p1' or 'p2'.</jc>
-	 * 	String json = s.serialize(<jk>new</jk> C3());
-	 * </p>
-	 *
-	 * <ul class='seealso'>
-	 * 	<li class='ja'>{@link Bean#stopClass()}
-	 * </ul>
-	 *
-	 * @param value The new value for this setting.
-	 * @return This object (for method chaining).
-	 */
-	public BeanFilterBuilder<T> stopClass(Class<?> value) {
-		this.stopClass = value;
-		return this;
-	}
-
-	/**
-	 * Configuration property:  Sort bean properties.
-	 *
-	 * <p>
-	 * When <jk>true</jk>, all bean properties will be serialized and access in alphabetical order.
-	 * <br>Otherwise, the natural order of the bean properties is used which is dependent on the JVM vendor.
-	 *
-	 * <h5 class='section'>Example:</h5>
-	 * <p class='bcode w800'>
-	 * 	<jc>// Define our filter.</jc>
-	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
-	 * 		<jk>public</jk> MyFilter() {
-	 * 			sortProperties();
-	 * 		}
-	 * 	}
-	 *
-	 * 	<jc>// Register it with a serializer.</jc>
-	 * 	WriterSerializer s = JsonSerializer
-	 * 		.<jsm>create</jsm>()
-	 * 		.beanFilters(MyFilter.<jk>class</jk>)
-	 * 		.build();
-	 *
-	 * 	<jc>// Properties will be sorted alphabetically.</jc>
-	 * 	String json = s.serialize(<jk>new</jk> MyBean());
-	 * </p>
-	 *
-	 * <ul class='seealso'>
-	 * 	<li class='ja'>{@link Bean#sort()}
-	 * 	<li class='jf'>{@link BeanContext#BEAN_sortProperties}
-	 * </ul>
-	 *
-	 * @param value
-	 * 	The new value for this property.
-	 * 	<br>The default is <jk>false</jk>.
-	 * @return This object (for method chaining).
-	 */
-	public BeanFilterBuilder<T> sortProperties(boolean value) {
-		this.sortProperties = value;
-		return this;
-	}
-
-	/**
-	 * Configuration property:  Sort bean properties.
-	 *
-	 * <p>
-	 * Shortcut for calling <code>sortProperties(<jk>true</jk>)</code>.
-	 *
-	 * <ul class='seealso'>
-	 * 	<li class='ja'>{@link Bean#sort()}
-	 * 	<li class='jf'>{@link BeanContext#BEAN_sortProperties}
-	 * </ul>
-	 *
-	 * @return This object (for method chaining).
-	 */
-	public BeanFilterBuilder<T> sortProperties() {
-		this.sortProperties = true;
-		return this;
-	}
-
-	/**
-	 * Configuration property:  Find fluent setters.
-	 *
-	 * <p>
-	 * When enabled, fluent setters are detected on beans.
-	 *
-	 * <p>
-	 * Fluent setters must have the following attributes:
-	 * <ul>
-	 * 	<li>Public.
-	 * 	<li>Not static.
-	 * 	<li>Take in one parameter.
-	 * 	<li>Return the bean itself.
-	 * </ul>
-	 *
-	 * <h5 class='section'>Example:</h5>
-	 * <p class='bcode w800'>
-	 * 	<jc>// Define our filter.</jc>
-	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
-	 * 		<jk>public</jk> MyFilter() {
-	 * 			fluentSetters();
-	 * 		}
-	 * 	}
-	 * </p>
-	 *
-	 * <ul class='seealso'>
-	 * 	<li class='ja'>{@link Bean#fluentSetters()}
-	 * 	<li class='jf'>{@link BeanContext#BEAN_fluentSetters}
-	 * </ul>
-	 *
-	 * @param value
-	 * 	The new value for this property.
-	 * 	<br>The default is <jk>false</jk>.
-	 * @return This object (for method chaining).
-	 */
-	public BeanFilterBuilder<T> fluentSetters(boolean value) {
-		this.fluentSetters = value;
-		return this;
-	}
-
-	/**
-	 * Configuration property:  Find fluent setters.
-	 *
-	 * <p>
-	 * Shortcut for calling <code>fluentSetters(<jk>true</jk>)</code>.
-	 *
-	 * <ul class='seealso'>
-	 * 	<li class='ja'>{@link Bean#fluentSetters()}
-	 * 	<li class='jf'>{@link BeanContext#BEAN_fluentSetters}
-	 * </ul>
-	 *
-	 * @return This object (for method chaining).
-	 */
-	public BeanFilterBuilder<T> fluentSetters() {
-		this.fluentSetters = true;
-		return this;
-	}
-
-	/**
-	 * Configuration property:  Bean property namer
-	 *
-	 * <p>
-	 * The class to use for calculating bean property names.
-	 *
-	 * <h5 class='section'>Example:</h5>
-	 * <p class='bcode w800'>
-	 * 	<jc>// Define our filter.</jc>
-	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
-	 * 		<jk>public</jk> MyFilter() {
-	 * 			<jc>// Use Dashed-Lower-Case property names.</jc>
-	 * 			<jc>// (e.g. "foo-bar-url" instead of "fooBarURL")</jc>
-	 * 			propertyNamer(PropertyNamerDLC.<jk>class</jk>);
-	 * 		}
-	 * 	}
-	 *
-	 * 	<jc>// Register it with a serializer or parser.</jc>
-	 * 	WriterSerializer s = JsonSerializer
-	 * 		.<jsm>create</jsm>()
-	 * 		.beanFilters(MyFilter.<jk>class</jk>)
-	 * 		.build();
-	 *
-	 * 	<jc>// Properties names will be Dashed-Lower-Case.</jc>
-	 * 	String json = s.serialize(<jk>new</jk> MyBean());
-	 * </p>
-	 *
-	 * <ul class='seealso'>
-	 * 	<li class='ja'>{@link Bean#propertyNamer()}
-	 * 	<li class='jf'>{@link BeanContext#BEAN_propertyNamer}
-	 * 	<li class='jc'>{@link PropertyNamer}
-	 * </ul>
-	 *
-	 * @param value
-	 * 	The new value for this setting.
-	 * 	<br>The default is {@link PropertyNamerDefault}.
-	 * @return This object (for method chaining).
-	 */
-	public BeanFilterBuilder<T> propertyNamer(Class<? extends PropertyNamer> value) {
-		this.propertyNamer = value;
-		return this;
-	}
-
-	/**
-	 * Configuration property:  Bean property includes.
-	 *
-	 * <p>
-	 * Specifies the set and order of names of properties associated with the bean class.
-	 *
-	 * <h5 class='section'>Example:</h5>
-	 * <p class='bcode w800'>
-	 * 	<jc>// Define our filter.</jc>
-	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
-	 * 		<jk>public</jk> MyFilter() {
-	 * 			bpi(<js>"foo,bar,baz"</js>);
-	 * 		}
-	 * 	}
-	 *
-	 * 	<jc>// Register it with a serializer.</jc>
-	 * 	WriterSerializer s = JsonSerializer
-	 * 		.<jsm>create</jsm>()
-	 * 		.beanFilters(MyFilter.<jk>class</jk>)
-	 * 		.build();
-	 *
-	 * 	<jc>// Only serializes the properties 'foo', 'bar', and 'baz'.</jc>
-	 * 	String json = s.serialize(<jk>new</jk> MyBean());
-	 * </p>
-	 *
-	 * <ul class='seealso'>
-	 * 	<li class='ja'>{@link Bean#bpi()}
-	 * 	<li class='jm'>{@link BeanContextBuilder#bpi(Class, String)}
-	 * 	<li class='jm'>{@link BeanContextBuilder#bpi(String, String)}
-	 * 	<li class='jm'>{@link BeanContextBuilder#bpi(Map)}
-	 * </ul>
-	 *
-	 * @param value
-	 * 	The new value for this setting.
-	 * 	<br>Values can contain comma-delimited list of property names.
-	 * @return This object (for method chaining).
-	 */
-	public BeanFilterBuilder<T> bpi(String...value) {
-		this.bpi = ASet.of();
-		for (String v : value)
-			bpi.a(split(v));
-		return this;
-	}
-
-	/**
-	 * Configuration property:  Bean property excludes.
-	 *
-	 * <p>
-	 * Specifies properties to exclude from the bean class.
-	 *
-	 * <h5 class='section'>Example:</h5>
-	 * <p class='bcode w800'>
-	 * 	<jc>// Define our filter.</jc>
-	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
-	 * 		<jk>public</jk> MyFilter() {
-	 * 			bpx(<js>"foo,bar"</js>);
-	 * 		}
-	 * 	}
-	 *
-	 * 	<jc>// Register it with a serializer.</jc>
-	 * 	WriterSerializer s = JsonSerializer
-	 * 		.<jsm>create</jsm>()
-	 * 		.beanFilters(MyFilter.<jk>class</jk>)
-	 * 		.build();
-	 *
-	 * 	<jc>// Serializes all properties except for 'foo' and 'bar'.</jc>
-	 * 	String json = s.serialize(<jk>new</jk> MyBean());
-	 * </p>
-	 *
-	 * <ul class='seealso'>
-	 * 	<li class='ja'>{@link Bean#bpx()}
-	 * 	<li class='jm'>{@link BeanContextBuilder#bpx(Class, String)}
-	 * 	<li class='jm'>{@link BeanContextBuilder#bpx(String, String)}
-	 * 	<li class='jm'>{@link BeanContextBuilder#bpx(Map)}
-	 * </ul>
-	 *
-	 * @param value
-	 * 	The new value for this setting.
-	 * 	<br>Values can contain comma-delimited list of property names.
-	 * @return This object (for method chaining).
-	 */
-	public BeanFilterBuilder<T> bpx(String...value) {
-		this.bpx = ASet.of();
-		for (String v : value)
-			bpx.a(split(v));
-		return this;
-	}
-
-	/**
-	 * Configuration property:  Read-only bean properties.
-	 *
-	 * <p>
-	 * Specifies one or more properties on a bean that are read-only despite having valid getters.
-	 * Serializers will serialize such properties as usual, but parsers will silently ignore them.
-	 *
-	 * <h5 class='section'>Example:</h5>
-	 * <p class='bcode w800'>
-	 * 	<jc>// Define our filter.</jc>
-	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
-	 * 		<jk>public</jk> MyFilter() {
-	 * 			bpro(<js>"foo,bar"</js>);
-	 * 		}
-	 * 	}
-	 *
-	 * 	<jc>// Register it with a parser.</jc>
-	 *  ReaderParser p = JsonParser
-	 * 		.<jsm>create</jsm>()
-	 * 		.beanFilters(MyFilter.<jk>class</jk>)
-	 * 		.build();
-	 *
-	 * 	<jc>// Parsers all properties except for 'foo' and 'bar'.</jc>
-	 * 	MyBean b = p.parse(<js>"..."</js>, MyBean.<jk>class</jk>);
-	 * </p>
-	 *
-	 * <ul class='seealso'>
-	 * 	<li class='ja'>{@link Bean#bpro()}
-	 * 	<li class='ja'>{@link Beanp#ro()}
-	 * 	<li class='jm'>{@link BeanContextBuilder#bpro(Class, String)}
-	 * 	<li class='jm'>{@link BeanContextBuilder#bpro(String, String)}
-	 * 	<li class='jm'>{@link BeanContextBuilder#bpro(Map)}
-	 * </ul>
-	 *
-	 * @param value
-	 * 	The new value for this setting.
-	 * 	<br>Values can contain comma-delimited list of property names.
-	 * @return This object (for method chaining).
-	 */
-	public BeanFilterBuilder<T> bpro(String...value) {
-		this.bpro = ASet.of();
-		for (String v : value)
-			bpro.a(split(v));
-		return this;
-	}
-
-	/**
-	 * Configuration property:  Write-only bean properties.
-	 *
-	 * <p>
-	 * Specifies one or more properties on a bean that are write-only despite having valid setters.
-	 * Parsers will parse such properties as usual, but serializers will silently ignore them.
-	 *
-	 * <h5 class='section'>Example:</h5>
-	 * <p class='bcode w800'>
-	 * 	<jc>// Define our filter.</jc>
-	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
-	 * 		<jk>public</jk> MyFilter() {
-	 * 			bpwo(<js>"foo,bar"</js>);
-	 * 		}
-	 * 	}
-	 *
-	 * 	<jc>// Register it with a serializer.</jc>
-	 *  WriterSerializer s = JsonSerializer
-	 * 		.<jsm>create</jsm>()
-	 * 		.beanFilters(MyFilter.<jk>class</jk>)
-	 * 		.build();
-	 *
-	 * 	<jc>// Serializes all properties except for 'foo' and 'bar'.</jc>
-	 * 	String json = s.serialize(<jk>new</jk> MyBean());
-	 * </p>
-	 *
-	 * <ul class='seealso'>
-	 * 	<li class='ja'>{@link Bean#bpwo()}
-	 * 	<li class='ja'>{@link Beanp#wo()}
-	 * 	<li class='jm'>{@link BeanContextBuilder#bpwo(Class, String)}
-	 * 	<li class='jm'>{@link BeanContextBuilder#bpwo(String, String)}
-	 * 	<li class='jm'>{@link BeanContextBuilder#bpwo(Map)}
-	 * </ul>
-	 *
-	 * @param value
-	 * 	The new value for this setting.
-	 * 	<br>Values can contain comma-delimited list of property names.
-	 * @return This object (for method chaining).
-	 */
-	public BeanFilterBuilder<T> bpwo(String...value) {
-		this.bpwo = ASet.of();
-		for (String v : value)
-			bpwo.a(split(v));
-		return this;
-	}
-
-	/**
-	 * Configuration property:  Bean dictionary.
-	 *
-	 * <p>
-	 * Adds to the list of classes that make up the bean dictionary for this bean.
-	 *
-	 * <h5 class='section'>Example:</h5>
-	 * <p class='bcode w800'>
-	 * 	<jc>// Define our filter.</jc>
-	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
-	 * 		<jk>public</jk> MyFilter() {
-	 * 			<jc>// Our bean contains generic collections of Foo and Bar objects.</jc>
-	 * 			beanDictionary(Foo.<jk>class</jk>, Bar.<jk>class</jk>);
-	 * 		}
-	 * 	}
-	 *
-	 * 	<jc>// Register it with a parser.</jc>
-	 * 	ReaderParser p = JsonParser
-	 * 		.<jsm>create</jsm>()
-	 * 		.beanFilters(MyFilter.<jk>class</jk>)
-	 * 		.build();
-	 *
-	 * 	<jc>// Instantiate our bean.</jc>
-	 * 	MyBean myBean = p.parse(json);
-	 * </p>
-	 *
-	 * <ul class='seealso'>
-	 * 	<li class='ja'>{@link Bean#dictionary()}
-	 * 	<li class='jf'>{@link BeanContext#BEAN_beanDictionary}
-	 * </ul>
-	 *
-	 * @param values
-	 * 	The values to add to this property.
-	 * @return This object (for method chaining).
-	 */
-	public BeanFilterBuilder<T> dictionary(Class<?>...values) {
-		if (dictionary == null)
-			dictionary = Arrays.asList(values);
-		else for (Class<?> cc : values)
-			dictionary.add(cc);
-		return this;
-	}
-
-	/**
-	 * Configuration property:  Bean interceptor.
-	 *
-	 * <p>
-	 * The interceptor to use for intercepting and altering getter and setter calls.
-	 *
-	 * <h5 class='section'>Example:</h5>
-	 * <p class='bcode w800'>
-	 * 	<jc>// Define our filter.</jc>
-	 * 	<jk>public class</jk> MyFilter <jk>extends</jk> BeanFilterBuilder&lt;MyBean&gt; {
-	 * 		<jk>public</jk> MyFilter() {
-	 * 			<jc>// Our bean contains generic collections of Foo and Bar objects.</jc>
-	 * 			interceptor(AddressInterceptor.<jk>class</jk>);
-	 * 		}
-	 * 	}
-	 *
-	 * 	<jc>// Register it with a serializer or parser.</jc>
-	 * 	WriterSerializer s = JsonSerializer
-	 * 		.<jsm>create</jsm>()
-	 * 		.beanFilters(MyFilter.<jk>class</jk>)
-	 * 		.build();
-	 * </p>
-	 *
-	 * <ul class='seealso'>
-	 * 	<li class='ja'>{@link Bean#interceptor()}
-	 * 	<li class='jc'>{@link BeanInterceptor}
-	 * </ul>
-	 *
-	 * @param value
-	 * 	The new value for this setting.
-	 * 	<br>The default value is {@link BeanInterceptor}.
-	 * @return This object (for method chaining).
-	 */
-	public BeanFilterBuilder<T> interceptor(Class<?> value) {
-		this.interceptor = value;
-		return this;
-	}
-
-	/**
-	 * Creates a {@link BeanFilter} with settings in this builder class.
-	 *
-	 * @return A new {@link BeanFilter} instance.
-	 */
-	public BeanFilter build() {
-		return new BeanFilter(this);
-	}
-}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/InterfaceBeanFilterBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/InterfaceBeanFilterBuilder.java
deleted file mode 100644
index 2f436bc..0000000
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/InterfaceBeanFilterBuilder.java
+++ /dev/null
@@ -1,99 +0,0 @@
-// ***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
-// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
-// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
-// * with the License.  You may obtain a copy of the License at                                                              *
-// *                                                                                                                         *
-// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
-// *                                                                                                                         *
-// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
-// * specific language governing permissions and limitations under the License.                                              *
-// ***************************************************************************************************************************
-package org.apache.juneau.transform;
-
-import static org.apache.juneau.internal.StringUtils.*;
-import java.util.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.annotation.*;
-import org.apache.juneau.reflect.*;
-
-/**
- * Simple bean filter that simply identifies a class to be used as an interface class for all child classes.
- *
- * <p>
- * These objects are created when you pass in non-<c>BeanFilterBuilder</c> classes to
- * {@link BeanContextBuilder#interfaceClass(Class, Class)}, and are equivalent to adding a
- * <code><ja>@Bean</ja>(interfaceClass=Foo.<jk>class</jk>)</code> annotation on the <c>Foo</c> class.
- *
- * @param <T> The interface class.
- */
-public class InterfaceBeanFilterBuilder<T> extends BeanFilterBuilder<T> {
-
-	/**
-	 * Constructor.
-	 *
-	 * <p>
-	 * Interface class is determined through reflection.
-	 */
-	protected InterfaceBeanFilterBuilder() {
-		init(beanClass, BeanContext.DEFAULT);
-	}
-
-	/**
-	 * Constructor.
-	 *
-	 * @param interfaceClass The class to use as an interface on all child classes.
-	 * @param bc The bean context to use for looking up annotations.
-	 */
-	public InterfaceBeanFilterBuilder(Class<T> interfaceClass, BeanContext bc) {
-		super(interfaceClass);
-		init(interfaceClass, bc);
-	}
-
-	private void init(Class<?> interfaceClass, BeanContext bc) {
-		interfaceClass(interfaceClass);
-		List<Bean> annotations = ClassInfo.of(interfaceClass).getAnnotations(Bean.class, bc);
-
-		for (Bean b : annotations) {
-
-			if (! b.bpi().isEmpty())
-				bpi(split(b.bpi()));
-
-			if (! b.bpx().isEmpty())
-				bpx(split(b.bpx()));
-
-			if (! b.bpro().isEmpty())
-				bpro(split(b.bpro()));
-
-			if (! b.bpwo().isEmpty())
-				bpwo(split(b.bpwo()));
-
-			if (! b.typeName().isEmpty())
-				typeName(b.typeName());
-
-			if (b.sort())
-				sortProperties(true);
-
-			if (b.fluentSetters())
-				fluentSetters(true);
-
-			try {
-				if (b.propertyNamer() != PropertyNamerDefault.class)
-					propertyNamer(b.propertyNamer());
-			} catch (Exception e) {
-				throw new RuntimeException(e);
-			}
-
-			if (b.interfaceClass() != Object.class)
-				interfaceClass(b.interfaceClass());
-
-			if (b.stopClass() != Object.class)
-				stopClass(b.stopClass());
-
-			if (b.dictionary().length > 0)
-				dictionary(b.dictionary());
-		}
-	}
-}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BeanFilter.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/UnmodifiableBeanFilter.java
similarity index 93%
copy from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BeanFilter.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/UnmodifiableBeanFilter.java
index f2e0d50..ea8825d 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BeanFilter.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/UnmodifiableBeanFilter.java
@@ -26,14 +26,14 @@ import org.apache.juneau.annotation.*;
  * Bean filters are used to control aspects of how beans are handled during serialization and parsing.
  *
  * <p>
- * Bean filters are created by {@link BeanFilterBuilder} which is the programmatic equivalent to the {@link Bean @Bean}
+ * Bean filters are created by {@link BeanFilter} which is the programmatic equivalent to the {@link Bean @Bean}
  * annotation.
  *
  * <ul class='seealso'>
  * 	<li class='link'>{@doc BeanFilters}
  * </ul>
  */
-public final class BeanFilter {
+public final class UnmodifiableBeanFilter {
 
 	private final Class<?> beanClass;
 	private final Set<String> bpi, bpx, bpro, bpwo;
@@ -48,7 +48,7 @@ public final class BeanFilter {
 	/**
 	 * Constructor.
 	 */
-	BeanFilter(BeanFilterBuilder<?> builder) {
+	UnmodifiableBeanFilter(BeanFilter<?> builder) {
 		this.beanClass = builder.beanClass;
 		this.typeName = builder.typeName;
 		this.bpi = new LinkedHashSet<>(builder.bpi);
@@ -77,8 +77,8 @@ public final class BeanFilter {
 	 * @param c The class being filtered.
 	 * @return A new instance of BeanFilterBuilder.
 	 */
-	public static <T> BeanFilterBuilder<T> create(Class<T> c) {
-		return new BeanFilterBuilder<>(c);
+	public static <T> BeanFilter<T> create(Class<T> c) {
+		return new BeanFilter<>(c);
 	}
 
 	/**
diff --git a/juneau-rest/juneau-rest-server-jaxrs/src/main/java/org/apache/juneau/rest/jaxrs/JuneauProvider.java b/juneau-rest/juneau-rest-server-jaxrs/src/main/java/org/apache/juneau/rest/jaxrs/JuneauProvider.java
index 8c62363..77b6e71 100644
--- a/juneau-rest/juneau-rest-server-jaxrs/src/main/java/org/apache/juneau/rest/jaxrs/JuneauProvider.java
+++ b/juneau-rest/juneau-rest-server-jaxrs/src/main/java/org/apache/juneau/rest/jaxrs/JuneauProvider.java
@@ -46,8 +46,8 @@ public @interface JuneauProvider {
 	 * These filters are applied to all serializers and parsers being used by the provider.
 	 *
 	 * <p>
-	 * If the specified class is an instance of {@link BeanFilterBuilder}, then a filter built from that builder is added.
-	 * Any other classes are wrapped in a {@link InterfaceBeanFilterBuilder} to indicate that subclasses should
+	 * If the specified class is an instance of {@link BeanFilter}, then a filter built from that builder is added.
+	 * Any other classes are wrapped in a {@link BeanFilter} with {@link BeanFilter#interfaceClass(Class)} to indicate that subclasses should
 	 * be treated as the specified class type.
 	 */
 	Class<?>[] beanFilters() default {};