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/11/05 00:03:08 UTC

[juneau] branch master updated: Clean up annotations

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 4ebff45  Clean up annotations
4ebff45 is described below

commit 4ebff45aa2bcba14eda6df552cdc50c5d21177ae
Author: JamesBognar <ja...@salesforce.com>
AuthorDate: Wed Nov 4 19:02:57 2020 -0500

    Clean up annotations
---
 ...ionTest.java => BeanConfigAnnotation_Test.java} |   14 +-
 .../java/org/apache/juneau/ComboSerializeTest.java |    4 +-
 .../juneau/SerializerPropertiesComboTest.java      |    2 +-
 .../juneau/a/rttests/RoundTripBeanMapsTest.java    |   24 +-
 .../apache/juneau/annotation/BeanBuilder_Test.java |  187 ++++
 .../juneau/annotation/BeanIgnoreBuilder_Test.java  |  122 +++
 .../{BeanIgnoreTest.java => BeanIgnore_Test.java}  |    2 +-
 .../{BeanAnnotationTest.java => Bean_Test.java}    |    2 +-
 .../juneau/annotation/BeancBuilder_Test.java       |  108 ++
 .../juneau/annotation/BeanpBuilder_Test.java       |  158 +++
 .../juneau/annotation/ExampleBuilder_Test.java     |  125 +++
 .../juneau/annotation/MarshalledBuilder_Test.java  |  126 +++
 .../annotation/NamePropertyBuilder_Test.java       |  111 ++
 .../annotation/ParentPropertyBuilder_Test.java     |  111 ++
 .../apache/juneau/annotation/SwapBuilder_Test.java |  139 +++
 .../apache/juneau/annotation/UriBuilder_Test.java  |  114 ++
 .../juneau/assertions/ArrayAssertion_Test.java     |    4 +-
 .../juneau/assertions/ByteArrayAssertion_Test.java |   12 +-
 .../assertions/CollectionAssertion_Test.java       |    4 +-
 .../assertions/FluentIntegerAssertion_Test.java    |   28 +-
 .../assertions/FluentLongAssertion_Test.java       |   28 +-
 .../juneau/assertions/ListAssertion_Test.java      |    4 +-
 .../juneau/assertions/ObjectAssertion_Test.java    |   10 +-
 .../juneau/assertions/StringAssertion_Test.java    |   18 +-
 .../juneau/assertions/ThrowableAssertion_Test.java |    6 +-
 .../juneau/csv/{CsvTest.java => Csv_Test.java}     |    2 +-
 .../juneau/csv/annotation/CsvBuilder_Test.java     |  114 ++
 .../CsvConfig_Test.java}                           |    6 +-
 .../{BasicHtmlTest.java => BasicHtml_Test.java}    |    4 +-
 ...ommonParserTest.java => CommonParser_Test.java} |    2 +-
 .../html/{CommonTest.java => Common_Test.java}     |    4 +-
 ...ionTest.java => HtmlConfigAnnotation_Test.java} |    2 +-
 ...Test.java => HtmlDocConfigAnnotation_Test.java} |    2 +-
 .../juneau/html/{HtmlTest.java => Html_Test.java}  |    6 +-
 .../juneau/html/annotation/HtmlBuilder_Test.java   |  146 +++
 .../html/annotation/HtmlLinkBuilder_Test.java      |  119 +++
 .../http/annotation/AnnotationUtils_Test.java      |  961 +++++++++--------
 .../juneau/http/annotation/BodyBuilder_Test.java   |  173 +++
 .../annotation/ContactBuilder_Test.java}           |  224 ++--
 .../http/annotation/FormDataBuilder_Test.java      |  371 +++++++
 .../annotation/HasFormDataBuilder_Test.java}       |   72 +-
 .../annotation/HasQueryBuilder_Test.java}          |   75 +-
 .../juneau/http/annotation/HeaderBuilder_Test.java |  370 +++++++
 .../annotation/LicenseBuilder_Test.java}           |  223 ++--
 .../juneau/http/annotation/PathBuilder_Test.java   |  345 ++++++
 .../juneau/http/annotation/QueryBuilder_Test.java  |  370 +++++++
 .../http/annotation/RequestBuilder_Test.java       |  126 +++
 .../http/annotation/ResponseBodyBuilder_Test.java  |  117 +++
 .../http/annotation/ResponseBuilder_Test.java      |  184 ++++
 .../annotation/ResponseHeaderBuilder_Test.java     |  338 ++++++
 .../annotation/ResponseStatusBuilder_Test.java     |  117 +++
 .../annotation/TagBuilder_Test.java}               |  229 ++--
 .../juneau/jena/annotation/RdfBuilder_Test.java    |  135 +++
 .../juneau/jso/annotation/JsoBuilder_Test.java     |  114 ++
 .../juneau/json/annotation/JsonBuilder_Test.java   |  119 +++
 .../annotation/ExternalDocsBuilder_Test.java}      |  134 +--
 .../jsonschema/annotation/ItemsBuilder_Test.java   |  255 +++++
 .../jsonschema/annotation/SchemaBuilder_Test.java  |  405 +++++++
 .../annotation/SubItemsBuilder_Test.java           |  255 +++++
 .../msgpack/annotation/MsgPackBuilder_Test.java    |  114 ++
 .../oapi/annotation/OpenApiBuilder_Test.java       |  114 ++
 .../annotation/PlainTextBuilder_Test.java          |  114 ++
 .../soap/annotation/SoapXmlBuilder_Test.java       |  114 ++
 .../transforms/SwapsAnnotationComboTest.java       |  170 ++-
 .../juneau/uon/annotation/UonBuilder_Test.java     |  114 ++
 .../annotation/UrlEncodingBuilder_Test.java        |  119 +++
 .../org/apache/juneau/utils/ArrayUtilsTest.java    |    4 +-
 .../org/apache/juneau/xml/XmlCollapsedTest.java    |    4 +-
 .../juneau/xml/annotation/XmlBuilder_Test.java     |  134 +++
 .../org/apache/juneau/jena/RdfParserSession.java   |    2 +
 .../org/apache/juneau/jena/annotation/Rdf.java     |    6 +-
 .../apache/juneau/jena/annotation/RdfArray.java}   |   59 +-
 .../apache/juneau/jena/annotation/RdfBuilder.java  |  197 ++++
 ...tyNamerDefault.java => BasicPropertyNamer.java} |    2 +-
 .../main/java/org/apache/juneau/BeanContext.java   |  204 +---
 .../java/org/apache/juneau/BeanContextBuilder.java |  116 +-
 .../java/org/apache/juneau/BeanFilterBuilder.java  |    8 +-
 .../src/main/java/org/apache/juneau/BeanMeta.java  |   15 +-
 .../java/org/apache/juneau/BeanMetaFiltered.java   |    2 +-
 .../main/java/org/apache/juneau/BeanSession.java   |   11 -
 .../src/main/java/org/apache/juneau/ClassMeta.java |   56 +-
 .../java/org/apache/juneau/ContextBuilder.java     |   38 -
 .../org/apache/juneau/PropertyStoreBuilder.java    |    6 +-
 .../annotation/{CC.java => AnnotationBuilder.java} |   23 +-
 .../apache/juneau/annotation/AnnotationImpl.java   |   97 ++
 .../java/org/apache/juneau/annotation/Bean.java    |  131 +--
 .../apache/juneau/annotation/BeanAnnotation.java   |  330 ------
 .../annotation/{Swaps.java => BeanArray.java}      |   25 +-
 .../org/apache/juneau/annotation/BeanBuilder.java  |  378 +++++++
 .../org/apache/juneau/annotation/BeanConfig.java   |   25 +-
 .../apache/juneau/annotation/BeanConfigApply.java  |   12 +-
 .../org/apache/juneau/annotation/BeanIgnore.java   |   17 +-
 .../annotation/{CC.java => BeanIgnoreArray.java}   |   58 +-
 ...ampleAnnotation.java => BeanIgnoreBuilder.java} |   91 +-
 .../java/org/apache/juneau/annotation/Beanc.java   |   10 +-
 .../juneau/annotation/{CC.java => BeancArray.java} |   58 +-
 .../{BeancAnnotation.java => BeancBuilder.java}    |   76 +-
 .../java/org/apache/juneau/annotation/Beanp.java   |  181 ++--
 .../apache/juneau/annotation/BeanpAnnotation.java  |  217 ----
 .../juneau/annotation/{CC.java => BeanpArray.java} |   58 +-
 .../org/apache/juneau/annotation/BeanpBuilder.java |  260 +++++
 .../java/org/apache/juneau/annotation/Example.java |    2 +
 .../annotation/{CC.java => ExampleArray.java}      |   58 +-
 .../{SwapAnnotation.java => ExampleBuilder.java}   |  118 +--
 .../org/apache/juneau/annotation/Marshalled.java   |  129 ++-
 .../annotation/{CC.java => MarshalledArray.java}   |   58 +-
 .../juneau/annotation/MarshalledBuilder.java       |  146 +++
 .../org/apache/juneau/annotation/NameProperty.java |   10 +-
 .../annotation/{CC.java => NamePropertyArray.java} |   58 +-
 ...oreAnnotation.java => NamePropertyBuilder.java} |   73 +-
 .../apache/juneau/annotation/ParentProperty.java   |   10 +-
 .../{CC.java => ParentPropertyArray.java}          |   58 +-
 ...eAnnotation.java => ParentPropertyBuilder.java} |   76 +-
 .../java/org/apache/juneau/annotation/Swap.java    |    7 +-
 .../juneau/annotation/{CC.java => SwapArray.java}  |   58 +-
 .../org/apache/juneau/annotation/SwapBuilder.java  |  197 ++++
 .../juneau/annotation/TargetedAnnotation.java      |  205 ----
 .../annotation/TargetedAnnotationBuilder.java      |   60 +-
 ...tation.java => TargetedAnnotationCBuilder.java} |   48 +-
 .../{CC.java => TargetedAnnotationImpl.java}       |   29 +-
 ...tation.java => TargetedAnnotationMBuilder.java} |   45 +-
 ...ation.java => TargetedAnnotationMFBuilder.java} |   57 +-
 ...tion.java => TargetedAnnotationMFCBuilder.java} |   46 +-
 .../TargetedAnnotationTBuilder.java}               |   69 +-
 .../{CC.java => TargetedAnnotationTImpl.java}      |   29 +-
 ...ation.java => TargetedAnnotationTMBuilder.java} |   55 +-
 ...tion.java => TargetedAnnotationTMFBuilder.java} |   64 +-
 ...ion.java => TargetedAnnotationTMFCBuilder.java} |   57 +-
 .../java/org/apache/juneau/annotation/Uri.java     |    9 +
 .../juneau/annotation/{CC.java => UriArray.java}   |   58 +-
 .../UriBuilder.java}                               |   88 +-
 .../juneau/assertions/FluentArrayAssertion.java    |    2 +-
 .../assertions/FluentCollectionAssertion.java      |    4 +-
 .../assertions/FluentComparableAssertion.java      |    8 +-
 .../juneau/assertions/FluentDateAssertion.java     |    6 +-
 .../juneau/assertions/FluentObjectAssertion.java   |    8 +-
 .../juneau/assertions/FluentStringAssertion.java   |   12 +-
 .../assertions/FluentThrowableAssertion.java       |    2 +-
 .../assertions/FluentZonedDateTimeAssertion.java   |    6 +-
 .../java/org/apache/juneau/csv/annotation/Csv.java |    8 +
 .../annotation/CsvArray.java}                      |   59 +-
 .../{CsvAnnotation.java => CsvBuilder.java}        |   81 +-
 .../org/apache/juneau/html/annotation/Html.java    |    8 +
 .../juneau/html/annotation/HtmlAnnotation.java     |  182 ----
 .../annotation/HtmlArray.java}                     |   59 +-
 .../apache/juneau/html/annotation/HtmlBuilder.java |  234 +++++
 .../apache/juneau/html/annotation/HtmlLink.java    |    9 +-
 .../annotation/HtmlLinkArray.java}                 |   59 +-
 ...tmlLinkAnnotation.java => HtmlLinkBuilder.java} |  107 +-
 .../org/apache/juneau/http/annotation/Body.java    |  242 +++--
 .../juneau/http/annotation/BodyAnnotation.java     |  208 ----
 ...{ResponseBodyAnnotation.java => BodyArray.java} |   59 +-
 .../apache/juneau/http/annotation/BodyBuilder.java |  312 ++++++
 .../org/apache/juneau/http/annotation/Contact.java |   24 +-
 .../annotation/ContactBuilder.java}                |  152 +--
 .../apache/juneau/http/annotation/FormData.java    | 1050 ++++++++++---------
 .../juneau/http/annotation/FormDataAnnotation.java |  837 ---------------
 ...ponseBodyAnnotation.java => FormDataArray.java} |   59 +-
 .../juneau/http/annotation/FormDataBuilder.java    |  990 ++++++++++++++++++
 .../apache/juneau/http/annotation/HasFormData.java |   12 +-
 ...DataAnnotation.java => HasFormDataBuilder.java} |   99 +-
 .../apache/juneau/http/annotation/HasQuery.java    |   12 +-
 .../annotation/HasQueryBuilder.java}               |  101 +-
 .../org/apache/juneau/http/annotation/Header.java  | 1050 ++++++++++---------
 .../juneau/http/annotation/HeaderAnnotation.java   |  837 ---------------
 ...esponseBodyAnnotation.java => HeaderArray.java} |   59 +-
 .../juneau/http/annotation/HeaderBuilder.java      |  991 ++++++++++++++++++
 .../juneau/http/annotation/LicenseAnnotation.java  |   77 --
 ...{ContactAnnotation.java => LicenseBuilder.java} |  112 +-
 .../org/apache/juneau/http/annotation/Path.java    |  960 ++++++++---------
 .../juneau/http/annotation/PathAnnotation.java     |  758 --------------
 ...esponseStatusAnnotation.java => PathArray.java} |   59 +-
 .../apache/juneau/http/annotation/PathBuilder.java |  906 ++++++++++++++++
 .../org/apache/juneau/http/annotation/Query.java   | 1082 +++++++++----------
 .../juneau/http/annotation/QueryAnnotation.java    |  837 ---------------
 ...ResponseBodyAnnotation.java => QueryArray.java} |   59 +-
 .../juneau/http/annotation/QueryBuilder.java       |  990 ++++++++++++++++++
 .../org/apache/juneau/http/annotation/Request.java |   30 +-
 .../juneau/http/annotation/RequestAnnotation.java  |   63 --
 ...sponseBodyAnnotation.java => RequestArray.java} |   59 +-
 .../juneau/http/annotation/RequestBuilder.java     |  148 +++
 .../apache/juneau/http/annotation/Response.java    |  276 ++---
 .../juneau/http/annotation/ResponseAnnotation.java |  244 -----
 ...ponseBodyAnnotation.java => ResponseArray.java} |   59 +-
 .../juneau/http/annotation/ResponseBody.java       |   26 +-
 ...eBodyAnnotation.java => ResponseBodyArray.java} |   59 +-
 .../annotation/ResponseBodyBuilder.java}           |   92 +-
 .../juneau/http/annotation/ResponseBuilder.java    |  354 +++++++
 .../juneau/http/annotation/ResponseHeader.java     |  584 ++++++-----
 .../http/annotation/ResponseHeaderAnnotation.java  |  741 -------------
 ...odyAnnotation.java => ResponseHeaderArray.java} |   59 +-
 .../http/annotation/ResponseHeaderBuilder.java     |  883 ++++++++++++++++
 .../juneau/http/annotation/ResponseStatus.java     |   25 +-
 ...odyAnnotation.java => ResponseStatusArray.java} |   59 +-
 .../annotation/ResponseStatusBuilder.java}         |   92 +-
 .../org/apache/juneau/http/annotation/Tag.java     |   20 +-
 .../juneau/http/annotation/TagAnnotation.java      |   95 --
 .../apache/juneau/http/annotation/TagBuilder.java} |  156 +--
 .../apache/juneau/internal/AnnotationUtils.java    |  186 ++++
 .../org/apache/juneau/internal/ArrayUtils.java     |   11 +
 .../org/apache/juneau/internal/ObjectUtils.java    |   61 +-
 .../java/org/apache/juneau/jso/annotation/Jso.java |    8 +
 .../annotation/JsoArray.java}                      |   59 +-
 .../{JsoAnnotation.java => JsoBuilder.java}        |   81 +-
 .../org/apache/juneau/json/annotation/Json.java    |    6 +-
 .../annotation/JsonArray.java}                     |   59 +-
 .../{JsonAnnotation.java => JsonBuilder.java}      |   98 +-
 ...ocsAnnotation.java => ExternalDocsBuilder.java} |   99 +-
 .../apache/juneau/jsonschema/annotation/Items.java |  174 +--
 .../jsonschema/annotation/ItemsAnnotation.java     |  632 -----------
 .../juneau/jsonschema/annotation/ItemsBuilder.java |  677 ++++++++++++
 .../juneau/jsonschema/annotation/Schema.java       |  764 +++++++-------
 .../jsonschema/annotation/SchemaAnnotation.java    | 1051 -------------------
 .../jsonschema/annotation/SchemaBuilder.java       | 1105 ++++++++++++++++++++
 .../juneau/jsonschema/annotation/SubItems.java     |  182 ++--
 ...ubItemsAnnotation.java => SubItemsBuilder.java} |  687 ++++++------
 .../apache/juneau/msgpack/annotation/MsgPack.java  |    8 +
 .../annotation/MsgPackArray.java}                  |   59 +-
 ...{MsgPackAnnotation.java => MsgPackBuilder.java} |   81 +-
 .../apache/juneau/oapi/OpenApiParserSession.java   |    4 +-
 .../org/apache/juneau/oapi/annotation/OpenApi.java |    8 +
 .../annotation/OpenApiArray.java}                  |   59 +-
 ...{OpenApiAnnotation.java => OpenApiBuilder.java} |   81 +-
 .../juneau/plaintext/annotation/PlainText.java     |    8 +
 .../annotation/PlainTextArray.java}                |   59 +-
 ...inTextAnnotation.java => PlainTextBuilder.java} |   81 +-
 .../java/org/apache/juneau/reflect/FieldInfo.java  |   23 +-
 .../org/apache/juneau/soap/annotation/SoapXml.java |    8 +
 .../annotation/SoapXmlArray.java}                  |   59 +-
 ...{SoapXmlAnnotation.java => SoapXmlBuilder.java} |   81 +-
 .../java/org/apache/juneau/transform/PojoSwap.java |    1 -
 .../java/org/apache/juneau/uon/annotation/Uon.java |    8 +
 .../annotation/UonArray.java}                      |   59 +-
 .../{UonAnnotation.java => UonBuilder.java}        |   81 +-
 .../juneau/urlencoding/annotation/UrlEncoding.java |    8 +
 .../annotation/UrlEncodingArray.java}              |   59 +-
 ...dingAnnotation.java => UrlEncodingBuilder.java} |  100 +-
 .../java/org/apache/juneau/xml/annotation/Xml.java |    7 +-
 .../annotation/XmlArray.java}                      |   59 +-
 .../apache/juneau/xml/annotation/XmlBuilder.java   |  194 ++++
 .../main/ConfigurablePropertyCodeGenerator.java    |   90 +-
 .../client/RestClient_Config_Context_Test.java     |    4 +-
 .../client/RestClient_Config_Serializer_Test.java  |    2 +-
 .../rest/annotation/LoggingBuilder_Test.java       |  108 ++
 .../rest/annotation/LoggingRuleBuilder_Test.java   |  123 +++
 .../rest/annotation/MethodSwaggerBuilder_Test.java |  144 +++
 .../annotation/ResourceSwaggerBuilder_Test.java    |  130 +++
 .../juneau/rest/annotation/RestBuilder_Test.java   |  380 +++++++
 .../rest/annotation/RestHookBuilder_Test.java      |  111 ++
 .../rest/annotation/RestMethodBuilder_Test.java    |  266 +++++
 .../java/org/apache/juneau/rest/RestContext.java   |    2 +-
 .../juneau/rest/RestMethodContextBuilder.java      |    2 +-
 .../org/apache/juneau/rest/annotation/Logging.java |    4 +
 .../juneau/rest/annotation/LoggingBuilder.java     |  162 +++
 .../apache/juneau/rest/annotation/LoggingRule.java |    4 +
 .../juneau/rest/annotation/LoggingRuleBuilder.java |  209 ++++
 .../rest/annotation/MethodAnnotationSwagger.java   |  105 --
 .../juneau/rest/annotation/MethodSwagger.java      |  159 +--
 .../rest/annotation/MethodSwaggerBuilder.java      |  284 +++++
 .../juneau/rest/annotation/ResourceSwagger.java    |  135 +--
 .../rest/annotation/ResourceSwaggerBuilder.java    |  240 +++++
 .../org/apache/juneau/rest/annotation/Rest.java    |  212 ++--
 .../apache/juneau/rest/annotation/RestArray.java   |   59 +-
 .../apache/juneau/rest/annotation/RestBuilder.java | 1035 ++++++++++++++++++
 .../apache/juneau/rest/annotation/RestHook.java    |   14 +-
 .../juneau/rest/annotation/RestHookArray.java      |   59 +-
 .../juneau/rest/annotation/RestHookBuilder.java    |   83 +-
 .../apache/juneau/rest/annotation/RestMethod.java  |  150 +--
 .../rest/annotation/RestMethodAnnotation.java      |  233 -----
 .../juneau/rest/annotation/RestMethodArray.java    |   59 +-
 .../juneau/rest/annotation/RestMethodBuilder.java  |  633 +++++++++++
 271 files changed, 28252 insertions(+), 15325 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/BeanConfigAnnotation_Test.java
similarity index 97%
rename from juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/BeanConfigAnnotationTest.java
rename to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/BeanConfigAnnotation_Test.java
index 7a91146..4bca047 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/BeanConfigAnnotation_Test.java
@@ -33,7 +33,7 @@ import org.junit.*;
  * Tests the @BeanConfig annotation.
  */
 @FixMethodOrder(NAME_ASCENDING)
-public class BeanConfigAnnotationTest {
+public class BeanConfigAnnotation_Test {
 
 	private static void check(String expected, Object o) {
 		assertEquals(expected, TO_STRING.apply(o));
@@ -132,7 +132,6 @@ public class BeanConfigAnnotationTest {
 		ignoreRecursions="$X{true}",
 		ignoreUnknownBeanProperties="$X{true}",
 		ignoreUnknownNullBeanProperties="$X{true}",
-		implClasses=@CC(k=A1.class,v=A1.class),
 		initialDepth="$X{1}",
 		locale="$X{en-US}",
 		maxDepth="$X{1}",
@@ -181,7 +180,6 @@ public class BeanConfigAnnotationTest {
 		check("true", bc.isIgnoreRecursions());
 		check("true", bc.isIgnoreUnknownBeanProperties());
 		check("true", bc.isIgnoreUnknownNullBeanProperties());
-		check("org.apache.juneau.BeanConfigAnnotationTest$A1=A1", bc.getImplClasses());
 		check("1", bc.getInitialDepth());
 		check("en_US", bc.getLocale());
 		check("1", bc.getMaxDepth());
@@ -229,14 +227,13 @@ public class BeanConfigAnnotationTest {
 		check("false", bc.isIgnoreRecursions());
 		check("false", bc.isIgnoreUnknownBeanProperties());
 		check("true", bc.isIgnoreUnknownNullBeanProperties());
-		check("", bc.getImplClasses());
 		check("0", bc.getInitialDepth());
 		check(Locale.getDefault().toString(), bc.getDefaultLocale());
 		check("100", bc.getMaxDepth());
 		check(null, bc.getDefaultMediaType());
 		check("java.lang,java.lang.annotation,java.lang.ref,java.lang.reflect,java.io,java.net", bc.getNotBeanPackagesNames());
 		check("", bc.getSwaps());
-		check("PropertyNamerDefault", bc.getPropertyNamer());
+		check("BasicPropertyNamer", bc.getPropertyNamer());
 		check("false", bc.isSortProperties());
 		check(null, bc.getDefaultTimeZone());
 		check("false", bc.isUseEnumNames());
@@ -275,14 +272,13 @@ public class BeanConfigAnnotationTest {
 		check("false", bc.isIgnoreRecursions());
 		check("false", bc.isIgnoreUnknownBeanProperties());
 		check("true", bc.isIgnoreUnknownNullBeanProperties());
-		check("", bc.getImplClasses());
 		check("0", bc.getInitialDepth());
 		check(Locale.getDefault().toString(), bc.getDefaultLocale());
 		check("100", bc.getMaxDepth());
 		check(null, bc.getDefaultMediaType());
 		check("java.lang,java.lang.annotation,java.lang.ref,java.lang.reflect,java.io,java.net", bc.getNotBeanPackagesNames());
 		check("", bc.getSwaps());
-		check("PropertyNamerDefault", bc.getPropertyNamer());
+		check("BasicPropertyNamer", bc.getPropertyNamer());
 		check("false", bc.isSortProperties());
 		check(null, bc.getDefaultTimeZone());
 		check("false", bc.isUseEnumNames());
@@ -335,7 +331,7 @@ public class BeanConfigAnnotationTest {
 
 	@Test
 	public void d03_beanBpiBpxCombined_beanContextBuilderOverride() throws Exception {
-		Bean ba = new BeanAnnotation("D").bpi("b,c,d").bpx("c");
+		Bean ba = BeanBuilder.create("D").bpi("b,c,d").bpx("c").build();
 		JsonSerializer js = JsonSerializer.create().simple().annotations(ba).build();
 		JsonParser jp = JsonParser.create().annotations(ba).build();
 
@@ -395,7 +391,7 @@ public class BeanConfigAnnotationTest {
 
 	@Test
 	public void e03_beanBpiBpxCombined_multipleBeanAnnotations_beanContextBuilderOverride() throws Exception {
-		Bean ba = new BeanAnnotation("E").bpi("b,c,d").bpx("c");
+		Bean ba = BeanBuilder.create("E").bpi("b,c,d").bpx("c").build();
 		JsonSerializer js = JsonSerializer.create().simple().annotations(ba).build();
 		JsonParser jp = JsonParser.create().annotations(ba).build();
 
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/ComboSerializeTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/ComboSerializeTest.java
index edef2ec..6ac0e0c 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/ComboSerializeTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/ComboSerializeTest.java
@@ -96,9 +96,9 @@ public abstract class ComboSerializeTest {
 
 			if (isRdf) {
 				Object[] args = { comboInput.label, testName };
-				assertString(r).msg("{0}/{1} serialize-normal failed", args).isEqualSortedLines(expected);
+				assertString(r).msg("{0}/{1} serialize-normal failed: <<<MSG>>>", args).isEqualSortedLines(expected);
 			} else
-				assertString(r).msg("{0}/{1} parse-normal failed", comboInput.label, testName).is(expected);
+				assertString(r).msg("{0}/{1} parse-normal failed: <<<MSG>>>", comboInput.label, testName).is(expected);
 
 		} catch (AssertionError e) {
 			if (comboInput.exceptionMsg == null)
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/SerializerPropertiesComboTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/SerializerPropertiesComboTest.java
index b4c98d0..50247c9 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/SerializerPropertiesComboTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/SerializerPropertiesComboTest.java
@@ -403,7 +403,7 @@ public class SerializerPropertiesComboTest extends ComboRoundTripTest {
 
 	public static class T5 {
 		public List<String> f1 = AList.of();
-		public String[] f2 = new String[0];
+		public String[] f2 = {};
 	}
 
 	public static class T6 {
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanMapsTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanMapsTest.java
index 78f3ef6..9a2d38d 100755
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanMapsTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanMapsTest.java
@@ -63,10 +63,6 @@ public class RoundTripBeanMapsTest extends RoundTripTest {
 		bean = roundTrip(bean, IBean.class);
 		assertEquals("bar", bean.getF1());
 
-		bean.setF1("baz");
-		bean = roundTrip(bean, ABean.class);
-		assertEquals("baz", bean.getF1());
-
 		bean.setF1("bing");
 		bean = roundTrip(bean, CBean.class);
 		assertEquals("bing", bean.getF1());
@@ -83,10 +79,6 @@ public class RoundTripBeanMapsTest extends RoundTripTest {
 		bean = roundTrip(bean, IBean[].class);
 		assertEquals("bar", bean[0].getF1());
 
-		bean[0].setF1("baz");
-		bean = roundTrip(bean, ABean[].class);
-		assertEquals("baz", bean[0].getF1());
-
 		bean[0].setF1("bing");
 		bean = roundTrip(bean, CBean[].class);
 		assertEquals("bing", bean[0].getF1());
@@ -105,12 +97,6 @@ public class RoundTripBeanMapsTest extends RoundTripTest {
 		l = roundTrip(l, LinkedList.class, IBean.class);
 		assertEquals("bar", l.get(0).getF1());
 
-		l.get(0).setF1("baz");
-		l = roundTrip(l, List.class, ABean.class);
-		assertEquals("baz", l.get(0).getF1());
-		l = roundTrip(l, LinkedList.class, ABean.class);
-		assertEquals("baz", l.get(0).getF1());
-
 		l.get(0).setF1("bing");
 		l = roundTrip(l, List.class, CBean.class);
 		assertEquals("bing", l.get(0).getF1());
@@ -131,12 +117,6 @@ public class RoundTripBeanMapsTest extends RoundTripTest {
 		l = roundTrip(l, LinkedHashMap.class, String.class, IBean.class);
 		assertEquals("bar", l.get("foo").getF1());
 
-		l.get("foo").setF1("baz");
-		l = roundTrip(l, Map.class, String.class, ABean.class);
-		assertEquals("baz", l.get("foo").getF1());
-		l = roundTrip(l, LinkedHashMap.class, String.class, ABean.class);
-		assertEquals("baz", l.get("foo").getF1());
-
 		l.get("foo").setF1("bing");
 		l = roundTrip(l, Map.class, String.class, CBean.class);
 		assertEquals("bing", l.get("foo").getF1());
@@ -415,8 +395,8 @@ public class RoundTripBeanMapsTest extends RoundTripTest {
 		if (isValidationOnly())
 			return;
 
-		Serializer s = getSerializer().builder().annotations(new BeanAnnotation(CA.class).dictionary(CAFilterDictionaryMap.class)).build();
-		Parser p = getParser().builder().annotations(new BeanAnnotation(CA.class).dictionary(CAFilterDictionaryMap.class)).build();
+		Serializer s = getSerializer().builder().annotations(BeanBuilder.create(CA.class).dictionary(CAFilterDictionaryMap.class).build()).build();
+		Parser p = getParser().builder().annotations(BeanBuilder.create(CA.class).dictionary(CAFilterDictionaryMap.class).build()).build();
 
 		CA1 c1 = CA1.create();
 		Object r = s.serialize(c1);
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanBuilder_Test.java
new file mode 100644
index 0000000..a40561e
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanBuilder_Test.java
@@ -0,0 +1,187 @@
+// ***************************************************************************************************************************
+// * 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.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.transform.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class BeanBuilder_Test {
+
+	private static final String CNAME = BeanBuilder_Test.class.getName();
+
+	private static class X1 {}
+	private  static class X2 extends BeanInterceptor<BeanBuilder_Test> {}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Bean a1 = BeanBuilder.create()
+		.bpi("bpi")
+		.bpro("bpro")
+		.bpwo("bpwo")
+		.bpx("bpx")
+		.dictionary(X1.class)
+		.example("example")
+		.fluentSetters(true)
+		.implClass(X1.class)
+		.interceptor(X2.class)
+		.interfaceClass(X1.class)
+		.on("on")
+		.onClass(X1.class)
+		.propertyNamer(BasicPropertyNamer.class)
+		.sort(true)
+		.stopClass(X1.class)
+		.typeName("typeName")
+		.typePropertyName("typePropertyName")
+		.build();
+
+	Bean a2 = BeanBuilder.create()
+		.bpi("bpi")
+		.bpro("bpro")
+		.bpwo("bpwo")
+		.bpx("bpx")
+		.dictionary(X1.class)
+		.example("example")
+		.fluentSetters(true)
+		.implClass(X1.class)
+		.interceptor(X2.class)
+		.interfaceClass(X1.class)
+		.on("on")
+		.onClass(X1.class)
+		.propertyNamer(BasicPropertyNamer.class)
+		.sort(true)
+		.stopClass(X1.class)
+		.typeName("typeName")
+		.typePropertyName("typePropertyName")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).stderr().json().is(""
+			+ "{"
+				+ "bpi:'bpi',"
+				+ "bpro:'bpro',"
+				+ "bpwo:'bpwo',"
+				+ "bpx:'bpx',"
+				+ "dictionary:['"+CNAME+"$X1'],"
+				+ "example:'example',"
+				+ "fluentSetters:true,"
+				+ "implClass:'"+CNAME+"$X1',"
+				+ "interceptor:'"+CNAME+"$X2',"
+				+ "interfaceClass:'"+CNAME+"$X1',"
+				+ "on:['on'],"
+				+ "onClass:['"+CNAME+"$X1'],"
+				+ "propertyNamer:'org.apache.juneau.BasicPropertyNamer',"
+				+ "sort:true,"
+				+ "stopClass:'"+CNAME+"$X1',"
+				+ "typeName:'typeName',"
+				+ "typePropertyName:'typePropertyName'"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext b1 = BeanContext.create().annotations(a1).build();
+		BeanContext b2 = BeanContext.create().annotations(a2).build();
+		assertTrue(b1 == b2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {}
+	public static class C2 {}
+
+	@Test
+	public void c01_otherMethods() {
+		Bean c1 = BeanBuilder.create(C1.class).on(C2.class).build();
+		Bean c2 = BeanBuilder.create("a").on("b").build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Bean(
+		bpi="bpi",
+		bpro="bpro",
+		bpwo="bpwo",
+		bpx="bpx",
+		dictionary=X1.class,
+		example="example",
+		fluentSetters=true,
+		implClass=X1.class,
+		interceptor=X2.class,
+		interfaceClass=X1.class,
+		on="on",
+		onClass=X1.class,
+		propertyNamer=BasicPropertyNamer.class,
+		sort=true,
+		stopClass=X1.class,
+		typeName="typeName",
+		typePropertyName="typePropertyName"
+	)
+	public static class D1 {}
+	Bean d1 = D1.class.getAnnotationsByType(Bean.class)[0];
+
+	@Bean(
+		bpi="bpi",
+		bpro="bpro",
+		bpwo="bpwo",
+		bpx="bpx",
+		dictionary=X1.class,
+		example="example",
+		fluentSetters=true,
+		implClass=X1.class,
+		interceptor=X2.class,
+		interfaceClass=X1.class,
+		on="on",
+		onClass=X1.class,
+		propertyNamer=BasicPropertyNamer.class,
+		sort=true,
+		stopClass=X1.class,
+		typeName="typeName",
+		typePropertyName="typePropertyName"
+	)
+	public static class D2 {}
+	Bean d2 = D2.class.getAnnotationsByType(Bean.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreBuilder_Test.java
new file mode 100644
index 0000000..298b32d
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreBuilder_Test.java
@@ -0,0 +1,122 @@
+// ***************************************************************************************************************************
+// * 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.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class BeanIgnoreBuilder_Test {
+
+	private static final String CNAME = BeanIgnoreBuilder_Test.class.getName();
+
+	private static class X1 {}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	BeanIgnore a1 = BeanIgnoreBuilder.create()
+		.on("a")
+		.onClass(X1.class)
+		.build();
+
+	BeanIgnore a2 = BeanIgnoreBuilder.create()
+		.on("a")
+		.onClass(X1.class)
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "on:['a'],"
+				+ "onClass:['"+CNAME+"$X1']"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		BeanIgnore c1 = BeanIgnoreBuilder.create(C1.class).on(C2.class).build();
+		BeanIgnore c2 = BeanIgnoreBuilder.create("a").on("b").build();
+		BeanIgnore c3 = BeanIgnoreBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		BeanIgnore c4 = BeanIgnoreBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+		BeanIgnore c5 = BeanIgnoreBuilder.create().on(C1.class.getConstructor()).on(C2.class.getConstructor()).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+		assertObject(c5).json().contains("on:['"+CNAME+"$C1()','"+CNAME+"$C2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@BeanIgnore(
+		on="a",
+		onClass=X1.class
+	)
+	public static class D1 {}
+	BeanIgnore d1 = D1.class.getAnnotationsByType(BeanIgnore.class)[0];
+
+	@BeanIgnore(
+		on="a",
+		onClass=X1.class
+	)
+	public static class D2 {}
+	BeanIgnore d2 = D2.class.getAnnotationsByType(BeanIgnore.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnore_Test.java
similarity index 96%
copy from juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreTest.java
copy to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnore_Test.java
index fe34da0..43c792e 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnore_Test.java
@@ -19,7 +19,7 @@ import org.apache.juneau.json.*;
 import org.junit.*;
 
 @FixMethodOrder(NAME_ASCENDING)
-public class BeanIgnoreTest {
+public class BeanIgnore_Test {
 
 	//------------------------------------------------------------------------------------------------------------------
 	// Test @BeanIgnore on properties
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanAnnotationTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/Bean_Test.java
similarity index 96%
rename from juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanAnnotationTest.java
rename to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/Bean_Test.java
index a97e6e6..cbd10ba 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanAnnotationTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/Bean_Test.java
@@ -21,7 +21,7 @@ import org.apache.juneau.reflect.*;
 import org.junit.*;
 
 @FixMethodOrder(NAME_ASCENDING)
-public class BeanAnnotationTest {
+public class Bean_Test {
 
 	//------------------------------------------------------------------------------------------------------------------
 	// @Bean annotation overrides visibility rules on class and constructor.
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeancBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeancBuilder_Test.java
new file mode 100644
index 0000000..802364e
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeancBuilder_Test.java
@@ -0,0 +1,108 @@
+// ***************************************************************************************************************************
+// * 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.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class BeancBuilder_Test {
+
+	private static final String CNAME = BeancBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Beanc a1 = BeancBuilder.create()
+		.on("on")
+		.properties("properties")
+		.build();
+
+	Beanc a2 = BeancBuilder.create()
+		.on("on")
+		.properties("properties")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "on:['on'],"
+				+ "properties:'properties'"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {}
+	public static class C2 {}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Beanc c1 = BeancBuilder.create("a").on("b").build();
+		Beanc c2 = BeancBuilder.create().on(C1.class.getConstructor()).on(C2.class.getConstructor()).build();
+
+		assertObject(c1).json().contains("on:['a','b']");
+		assertObject(c2).json().contains("on:['"+CNAME+"$C1()','"+CNAME+"$C2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Beanc(
+		on="on",
+		properties="properties"
+	)
+	public static class D1 {}
+	Beanc d1 = D1.class.getAnnotationsByType(Beanc.class)[0];
+
+	@Beanc(
+		on="on",
+		properties="properties"
+	)
+	public static class D2 {}
+	Beanc d2 = D2.class.getAnnotationsByType(Beanc.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanpBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanpBuilder_Test.java
new file mode 100644
index 0000000..a1f140f
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanpBuilder_Test.java
@@ -0,0 +1,158 @@
+// ***************************************************************************************************************************
+// * 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.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class BeanpBuilder_Test {
+
+	private static final String CNAME = BeanpBuilder_Test.class.getName();
+
+	public static class X1 {}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Beanp a1 = BeanpBuilder.create()
+		.dictionary(X1.class)
+		.format("format")
+		.name("name")
+		.on("on")
+		.params(X1.class)
+		.properties("properties")
+		.ro("ro")
+		.type(X1.class)
+		.value("value")
+		.wo("wo")
+		.build();
+
+	Beanp a2 = BeanpBuilder.create()
+		.dictionary(X1.class)
+		.format("format")
+		.name("name")
+		.on("on")
+		.params(X1.class)
+		.properties("properties")
+		.ro("ro")
+		.type(X1.class)
+		.value("value")
+		.wo("wo")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "dictionary:['"+CNAME+"$X1'],"
+				+ "format:'format',"
+				+ "name:'name',"
+				+ "on:['on'],"
+				+ "params:['"+CNAME+"$X1'],"
+				+ "properties:'properties',"
+				+ "ro:'ro',"
+				+ "type:'"+CNAME+"$X1',"
+				+ "value:'value',"
+				+ "wo:'wo'"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Beanp c1 = BeanpBuilder.create("a").on("b").build();
+		Beanp c2 = BeanpBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		Beanp c3 = BeanpBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['a','b']");
+		assertObject(c2).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Beanp(
+		dictionary=X1.class,
+		format="format",
+		name="name",
+		on="on",
+		params=X1.class,
+		properties="properties",
+		ro="ro",
+		type=X1.class,
+		value="value",
+		wo="wo"
+	)
+	public static class D1 {}
+	Beanp d1 = D1.class.getAnnotationsByType(Beanp.class)[0];
+
+	@Beanp(
+		dictionary=X1.class,
+		format="format",
+		name="name",
+		on="on",
+		params=X1.class,
+		properties="properties",
+		ro="ro",
+		type=X1.class,
+		value="value",
+		wo="wo"
+	)
+	public static class D2 {}
+	Beanp d2 = D2.class.getAnnotationsByType(Beanp.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/ExampleBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/ExampleBuilder_Test.java
new file mode 100644
index 0000000..f1c00ba
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/ExampleBuilder_Test.java
@@ -0,0 +1,125 @@
+// ***************************************************************************************************************************
+// * 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.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class ExampleBuilder_Test {
+
+	private static final String CNAME = ExampleBuilder_Test.class.getName();
+
+	private static class X1 {}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Example a1 = ExampleBuilder.create()
+		.on("on")
+		.onClass(X1.class)
+		.value("value")
+		.build();
+
+	Example a2 = ExampleBuilder.create()
+		.on("on")
+		.onClass(X1.class)
+		.value("value")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "on:['on'],"
+				+ "onClass:['"+CNAME+"$X1'],"
+				+ "value:'value'"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Example c1 = ExampleBuilder.create(C1.class).on(C2.class).build();
+		Example c2 = ExampleBuilder.create("a").on("b").build();
+		Example c3 = ExampleBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		Example c4 = ExampleBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Example(
+		on="on",
+		onClass=X1.class,
+		value="value"
+	)
+	public static class D1 {}
+	Example d1 = D1.class.getAnnotationsByType(Example.class)[0];
+
+	@Example(
+		on="on",
+		onClass=X1.class,
+		value="value"
+	)
+	public static class D2 {}
+	Example d2 = D2.class.getAnnotationsByType(Example.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/MarshalledBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/MarshalledBuilder_Test.java
new file mode 100644
index 0000000..41f2fa2
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/MarshalledBuilder_Test.java
@@ -0,0 +1,126 @@
+// ***************************************************************************************************************************
+// * 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.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class MarshalledBuilder_Test {
+
+	private static final String CNAME = MarshalledBuilder_Test.class.getName();
+
+	private static class X1 {}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Marshalled a1 = MarshalledBuilder.create()
+		.example("example")
+		.implClass(X1.class)
+		.on("on")
+		.onClass(X1.class)
+		.build();
+
+	Marshalled a2 = MarshalledBuilder.create()
+		.example("example")
+		.implClass(X1.class)
+		.on("on")
+		.onClass(X1.class)
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "example:'example',"
+				+ "implClass:'"+CNAME+"$X1',"
+				+ "on:['on'],"
+				+ "onClass:['"+CNAME+"$X1']"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Marshalled c1 = MarshalledBuilder.create(C1.class).on(C2.class).build();
+		Marshalled c2 = MarshalledBuilder.create("a").on("b").build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Marshalled(
+		example="example",
+		implClass=X1.class,
+		on="on",
+		onClass=X1.class
+	)
+	public static class D1 {}
+	Marshalled d1 = D1.class.getAnnotationsByType(Marshalled.class)[0];
+
+	@Marshalled(
+		example="example",
+		implClass=X1.class,
+		on="on",
+		onClass=X1.class
+	)
+	public static class D2 {}
+	Marshalled d2 = D2.class.getAnnotationsByType(Marshalled.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/NamePropertyBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/NamePropertyBuilder_Test.java
new file mode 100644
index 0000000..693b19f
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/NamePropertyBuilder_Test.java
@@ -0,0 +1,111 @@
+// ***************************************************************************************************************************
+// * 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.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class NamePropertyBuilder_Test {
+
+	private static final String CNAME = NamePropertyBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	NameProperty a1 = NamePropertyBuilder.create()
+		.on("on")
+		.build();
+
+	NameProperty a2 = NamePropertyBuilder.create()
+		.on("on")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "on:['on']"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		NameProperty c1 = NamePropertyBuilder.create("a").on("b").build();
+		NameProperty c2 = NamePropertyBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		NameProperty c3 = NamePropertyBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['a','b']");
+		assertObject(c2).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@NameProperty(
+		on="on"
+	)
+	public static class D1 {}
+	NameProperty d1 = D1.class.getAnnotationsByType(NameProperty.class)[0];
+
+	@NameProperty(
+		on="on"
+	)
+	public static class D2 {}
+	NameProperty d2 = D2.class.getAnnotationsByType(NameProperty.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/ParentPropertyBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/ParentPropertyBuilder_Test.java
new file mode 100644
index 0000000..143b1cc
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/ParentPropertyBuilder_Test.java
@@ -0,0 +1,111 @@
+// ***************************************************************************************************************************
+// * 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.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class ParentPropertyBuilder_Test {
+
+	private static final String CNAME = ParentPropertyBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	ParentProperty a1 = ParentPropertyBuilder.create()
+		.on("on")
+		.build();
+
+	ParentProperty a2 = ParentPropertyBuilder.create()
+		.on("on")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "on:['on']"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		ParentProperty c1 = ParentPropertyBuilder.create("a").on("b").build();
+		ParentProperty c2 = ParentPropertyBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		ParentProperty c3 = ParentPropertyBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['a','b']");
+		assertObject(c2).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@ParentProperty(
+		on="on"
+	)
+	public static class D1 {}
+	ParentProperty d1 = D1.class.getAnnotationsByType(ParentProperty.class)[0];
+
+	@ParentProperty(
+		on="on"
+	)
+	public static class D2 {}
+	ParentProperty d2 = D2.class.getAnnotationsByType(ParentProperty.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/SwapBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/SwapBuilder_Test.java
new file mode 100644
index 0000000..d2c8ecc
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/SwapBuilder_Test.java
@@ -0,0 +1,139 @@
+// ***************************************************************************************************************************
+// * 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.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class SwapBuilder_Test {
+
+	private static final String CNAME = SwapBuilder_Test.class.getName();
+
+	private static class X1 {}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Swap a1 = SwapBuilder.create()
+		.impl(X1.class)
+		.mediaTypes("mediaTypes")
+		.on("on")
+		.onClass(X1.class)
+		.template("template")
+		.value(X1.class)
+		.build();
+
+	Swap a2 = SwapBuilder.create()
+		.impl(X1.class)
+		.mediaTypes("mediaTypes")
+		.on("on")
+		.onClass(X1.class)
+		.template("template")
+		.value(X1.class)
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "impl:'"+CNAME+"$X1',"
+				+ "mediaTypes:['mediaTypes'],"
+				+ "on:['on'],"
+				+ "onClass:['"+CNAME+"$X1'],"
+				+ "template:'template',"
+				+ "value:'"+CNAME+"$X1'"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Swap c1 = SwapBuilder.create(C1.class).on(C2.class).build();
+		Swap c2 = SwapBuilder.create("a").on("b").build();
+		Swap c3 = SwapBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		Swap c4 = SwapBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Swap(
+		impl=X1.class,
+		mediaTypes="mediaTypes",
+		on="on",
+		onClass=X1.class,
+		template="template",
+		value=X1.class
+	)
+	public static class D1 {}
+	Swap d1 = D1.class.getAnnotationsByType(Swap.class)[0];
+
+	@Swap(
+		impl=X1.class,
+		mediaTypes="mediaTypes",
+		on="on",
+		onClass=X1.class,
+		template="template",
+		value=X1.class
+	)
+	public static class D2 {}
+	Swap d2 = D2.class.getAnnotationsByType(Swap.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/UriBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/UriBuilder_Test.java
new file mode 100644
index 0000000..e2017d0
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/UriBuilder_Test.java
@@ -0,0 +1,114 @@
+// ***************************************************************************************************************************
+// * 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.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class UriBuilder_Test {
+
+	private static final String CNAME = UriBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Uri a1 = UriBuilder.create()
+		.on("a")
+		.build();
+
+	Uri a2 = UriBuilder.create()
+		.on("a")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "on:['a'],"
+				+ "onClass:[]"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Uri c1 = UriBuilder.create(C1.class).on(C2.class).build();
+		Uri c2 = UriBuilder.create("a").on("b").build();
+		Uri c3 = UriBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		Uri c4 = UriBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Uri(
+		on="a"
+	)
+	public static class D1 {}
+	Uri d1 = D1.class.getAnnotationsByType(Uri.class)[0];
+
+	@Uri(
+		on="a"
+	)
+	public static class D2 {}
+	Uri d2 = D2.class.getAnnotationsByType(Uri.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ArrayAssertion_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ArrayAssertion_Test.java
index a863e0a..acc759f 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ArrayAssertion_Test.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ArrayAssertion_Test.java
@@ -32,9 +32,9 @@ public class ArrayAssertion_Test {
 
 		assertThrown(()->assertArray(null).isSize(0)).is("Value was null.");
 		assertArray(x1).isSize(0);
-		assertThrown(()->assertArray(x1).isSize(1)).is("Array did not have the expected size.  Expected=1, Actual=0.");
+		assertThrown(()->assertArray(x1).isSize(1)).is("Array did not have the expected size.  Expect=1, Actual=0.");
 		assertArray(x2).isSize(1);
-		assertThrown(()->assertArray(x2).isSize(0)).is("Array did not have the expected size.  Expected=0, Actual=1.");
+		assertThrown(()->assertArray(x2).isSize(0)).is("Array did not have the expected size.  Expect=0, Actual=1.");
 
 		assertThrown(()->assertArray(null).isEmpty()).is("Value was null.");
 		assertArray(x1).isEmpty();
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ByteArrayAssertion_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ByteArrayAssertion_Test.java
index c2fa11b..47fceb3 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ByteArrayAssertion_Test.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ByteArrayAssertion_Test.java
@@ -32,9 +32,9 @@ public class ByteArrayAssertion_Test {
 
 		assertThrown(()->assertBytes(null).isSize(0)).is("Value was null.");
 		assertBytes(x1).isSize(0);
-		assertThrown(()->assertBytes(x1).isSize(1)).is("Array did not have the expected size.  Expected=1, Actual=0.");
+		assertThrown(()->assertBytes(x1).isSize(1)).is("Array did not have the expected size.  Expect=1, Actual=0.");
 		assertBytes(x2).isSize(2);
-		assertThrown(()->assertBytes(x2).isSize(0)).is("Array did not have the expected size.  Expected=0, Actual=2.");
+		assertThrown(()->assertBytes(x2).isSize(0)).is("Array did not have the expected size.  Expect=0, Actual=2.");
 
 		assertThrown(()->assertBytes(null).isEmpty()).is("Value was null.");
 		assertBytes(x1).isEmpty();
@@ -51,22 +51,22 @@ public class ByteArrayAssertion_Test {
 		assertBytes(null).string().isNull();
 		assertBytes(x1).string().is("");
 		assertBytes(x2).string().is("ab");
-		assertThrown(()->assertBytes(x2).string().is("xx")).is("Unexpected value.\n\tExpected=[xx]\n\tActual=[ab]");
+		assertThrown(()->assertBytes(x2).string().is("xx")).is("Unexpected value.\n\tExpect=[xx]\n\tActual=[ab]");
 
 		assertBytes(null).base64().isNull();
 		assertBytes(x1).base64().is("");
 		assertBytes(x2).base64().is("YWI=");
-		assertThrown(()->assertBytes(x2).base64().is("xx")).is("Unexpected value.\n\tExpected=[xx]\n\tActual=[YWI=]");
+		assertThrown(()->assertBytes(x2).base64().is("xx")).is("Unexpected value.\n\tExpect=[xx]\n\tActual=[YWI=]");
 
 		assertBytes(null).hex().isNull();
 		assertBytes(x1).hex().is("");
 		assertBytes(x2).hex().is("6162");
-		assertThrown(()->assertBytes(x2).hex().is("xx")).is("Unexpected value.\n\tExpected=[xx]\n\tActual=[6162]");
+		assertThrown(()->assertBytes(x2).hex().is("xx")).is("Unexpected value.\n\tExpect=[xx]\n\tActual=[6162]");
 
 		assertBytes(null).spacedHex().isNull();
 		assertBytes(x1).spacedHex().is("");
 		assertBytes(x2).spacedHex().is("61 62");
-		assertThrown(()->assertBytes(x2).spacedHex().is("xx")).is("Unexpected value.\n\tExpected=[xx]\n\tActual=[61 62]");
+		assertThrown(()->assertBytes(x2).spacedHex().is("xx")).is("Unexpected value.\n\tExpect=[xx]\n\tActual=[61 62]");
 	}
 
 	@Test
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/CollectionAssertion_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/CollectionAssertion_Test.java
index 431ff6e..e90a5d0 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/CollectionAssertion_Test.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/CollectionAssertion_Test.java
@@ -35,9 +35,9 @@ public class CollectionAssertion_Test {
 
 		assertThrown(()->assertCollection(null).isSize(0)).is("Value was null.");
 		assertCollection(x1).isSize(0);
-		assertThrown(()->assertCollection(x1).isSize(1)).is("Collection did not have the expected size.  Expected=1, Actual=0.");
+		assertThrown(()->assertCollection(x1).isSize(1)).is("Collection did not have the expected size.  Expect=1, Actual=0.");
 		assertCollection(x2).isSize(2);
-		assertThrown(()->assertCollection(x2).isSize(0)).is("Collection did not have the expected size.  Expected=0, Actual=2.");
+		assertThrown(()->assertCollection(x2).isSize(0)).is("Collection did not have the expected size.  Expect=0, Actual=2.");
 
 		assertThrown(()->assertCollection(null).isEmpty()).is("Value was null.");
 		assertCollection(x1).isEmpty();
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/FluentIntegerAssertion_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/FluentIntegerAssertion_Test.java
index fbd8986..4ea614e 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/FluentIntegerAssertion_Test.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/FluentIntegerAssertion_Test.java
@@ -38,49 +38,49 @@ public class FluentIntegerAssertion_Test {
 		assertThrown(()->assertInteger(null).isGreaterThan(1)).is("Value was null.");
 		assertThrown(()->assertInteger(1).isGreaterThan(null)).is("Parameter 'value' cannot be null.");
 		assertInteger(2).isGreaterThan(1);
-		assertThrown(()->assertInteger(1).isGreaterThan(2)).is("Value was not greater than expected.\n\tExpected=[2]\n\tActual=[1]");
-		assertThrown(()->assertInteger(1).isGreaterThan(1)).is("Value was not greater than expected.\n\tExpected=[1]\n\tActual=[1]");
+		assertThrown(()->assertInteger(1).isGreaterThan(2)).is("Value was not greater than expected.\n\tExpect=[2]\n\tActual=[1]");
+		assertThrown(()->assertInteger(1).isGreaterThan(1)).is("Value was not greater than expected.\n\tExpect=[1]\n\tActual=[1]");
 
 		assertThrown(()->assertInteger(null).isGt(1)).is("Value was null.");
 		assertThrown(()->assertInteger(1).isGt(null)).is("Parameter 'value' cannot be null.");
 		assertInteger(2).isGt(1);
-		assertThrown(()->assertInteger(1).isGt(2)).is("Value was not greater than expected.\n\tExpected=[2]\n\tActual=[1]");
-		assertThrown(()->assertInteger(1).isGt(1)).is("Value was not greater than expected.\n\tExpected=[1]\n\tActual=[1]");
+		assertThrown(()->assertInteger(1).isGt(2)).is("Value was not greater than expected.\n\tExpect=[2]\n\tActual=[1]");
+		assertThrown(()->assertInteger(1).isGt(1)).is("Value was not greater than expected.\n\tExpect=[1]\n\tActual=[1]");
 
 		assertThrown(()->assertInteger(null).isGreaterThanOrEqual(1)).is("Value was null.");
 		assertThrown(()->assertInteger(1).isGreaterThanOrEqual(null)).is("Parameter 'value' cannot be null.");
 		assertInteger(2).isGreaterThanOrEqual(1);
-		assertThrown(()->assertInteger(1).isGreaterThanOrEqual(2)).is("Value was not greater than or equals to expected.\n\tExpected=[2]\n\tActual=[1]");
+		assertThrown(()->assertInteger(1).isGreaterThanOrEqual(2)).is("Value was not greater than or equals to expected.\n\tExpect=[2]\n\tActual=[1]");
 		assertInteger(1).isGreaterThanOrEqual(1);
 
 		assertThrown(()->assertInteger(null).isGte(1)).is("Value was null.");
 		assertThrown(()->assertInteger(1).isGte(null)).is("Parameter 'value' cannot be null.");
 		assertInteger(2).isGte(1);
-		assertThrown(()->assertInteger(1).isGte(2)).is("Value was not greater than or equals to expected.\n\tExpected=[2]\n\tActual=[1]");
+		assertThrown(()->assertInteger(1).isGte(2)).is("Value was not greater than or equals to expected.\n\tExpect=[2]\n\tActual=[1]");
 		assertInteger(1).isGte(1);
 
 		assertThrown(()->assertInteger(null).isLessThan(1)).is("Value was null.");
 		assertThrown(()->assertInteger(1).isLessThan(null)).is("Parameter 'value' cannot be null.");
 		assertInteger(1).isLessThan(2);
-		assertThrown(()->assertInteger(2).isLessThan(1)).is("Value was not less than expected.\n\tExpected=[1]\n\tActual=[2]");
-		assertThrown(()->assertInteger(1).isLessThan(1)).is("Value was not less than expected.\n\tExpected=[1]\n\tActual=[1]");
+		assertThrown(()->assertInteger(2).isLessThan(1)).is("Value was not less than expected.\n\tExpect=[1]\n\tActual=[2]");
+		assertThrown(()->assertInteger(1).isLessThan(1)).is("Value was not less than expected.\n\tExpect=[1]\n\tActual=[1]");
 
 		assertThrown(()->assertInteger(null).isLt(1)).is("Value was null.");
 		assertThrown(()->assertInteger(1).isLt(null)).is("Parameter 'value' cannot be null.");
 		assertInteger(1).isLt(2);
-		assertThrown(()->assertInteger(2).isLt(1)).is("Value was not less than expected.\n\tExpected=[1]\n\tActual=[2]");
-		assertThrown(()->assertInteger(1).isLt(1)).is("Value was not less than expected.\n\tExpected=[1]\n\tActual=[1]");
+		assertThrown(()->assertInteger(2).isLt(1)).is("Value was not less than expected.\n\tExpect=[1]\n\tActual=[2]");
+		assertThrown(()->assertInteger(1).isLt(1)).is("Value was not less than expected.\n\tExpect=[1]\n\tActual=[1]");
 
 		assertThrown(()->assertInteger(null).isLessThanOrEqual(1)).is("Value was null.");
 		assertThrown(()->assertInteger(1).isLessThanOrEqual(null)).is("Parameter 'value' cannot be null.");
 		assertInteger(1).isLessThanOrEqual(2);
-		assertThrown(()->assertInteger(2).isLessThanOrEqual(1)).is("Value was not less than or equals to expected.\n\tExpected=[1]\n\tActual=[2]");
+		assertThrown(()->assertInteger(2).isLessThanOrEqual(1)).is("Value was not less than or equals to expected.\n\tExpect=[1]\n\tActual=[2]");
 		assertInteger(1).isLessThanOrEqual(1);
 
 		assertThrown(()->assertInteger(null).isLte(1)).is("Value was null.");
 		assertThrown(()->assertInteger(1).isLte(null)).is("Parameter 'value' cannot be null.");
 		assertInteger(1).isLte(2);
-		assertThrown(()->assertInteger(2).isLte(1)).is("Value was not less than or equals to expected.\n\tExpected=[1]\n\tActual=[2]");
+		assertThrown(()->assertInteger(2).isLte(1)).is("Value was not less than or equals to expected.\n\tExpect=[1]\n\tActual=[2]");
 		assertInteger(1).isLte(1);
 
 		assertThrown(()->assertInteger(null).isBetween(1,3)).is("Value was null.");
@@ -89,8 +89,8 @@ public class FluentIntegerAssertion_Test {
 		assertInteger(2).isBetween(1,3);
 		assertInteger(1).isBetween(1,3);
 		assertInteger(3).isBetween(1,3);
-		assertThrown(()->assertInteger(2).isBetween(1,1)).is("Value was not less than or equals to expected.\n\tExpected=[1]\n\tActual=[2]");
-		assertThrown(()->assertInteger(2).isBetween(3,3)).is("Value was not greater than or equals to expected.\n\tExpected=[3]\n\tActual=[2]");
+		assertThrown(()->assertInteger(2).isBetween(1,1)).is("Value was not less than or equals to expected.\n\tExpect=[1]\n\tActual=[2]");
+		assertThrown(()->assertInteger(2).isBetween(3,3)).is("Value was not greater than or equals to expected.\n\tExpect=[3]\n\tActual=[2]");
 	}
 
 	@Test
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/FluentLongAssertion_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/FluentLongAssertion_Test.java
index bb51397..22b6515 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/FluentLongAssertion_Test.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/FluentLongAssertion_Test.java
@@ -38,8 +38,8 @@ public class FluentLongAssertion_Test {
 		assertThrown(()->assertLong(null).isGreaterThan(1)).is("Value was null.");
 		assertThrown(()->assertLong(1l).isGreaterThan(null)).is("Parameter 'value' cannot be null.");
 		assertLong(2l).isGreaterThan(1);
-		assertThrown(()->assertLong(1l).isGreaterThan(2l)).is("Value was not greater than expected.\n\tExpected=[2]\n\tActual=[1]");
-		assertThrown(()->assertLong(1l).isGreaterThan(1l)).is("Value was not greater than expected.\n\tExpected=[1]\n\tActual=[1]");
+		assertThrown(()->assertLong(1l).isGreaterThan(2l)).is("Value was not greater than expected.\n\tExpect=[2]\n\tActual=[1]");
+		assertThrown(()->assertLong(1l).isGreaterThan(1l)).is("Value was not greater than expected.\n\tExpect=[1]\n\tActual=[1]");
 
 		assertLong(2l).integer().isGreaterThan(1);
 		assertLong(null).integer().isNull();
@@ -47,43 +47,43 @@ public class FluentLongAssertion_Test {
 		assertThrown(()->assertLong(null).isGt(1l)).is("Value was null.");
 		assertThrown(()->assertLong(1l).isGt(null)).is("Parameter 'value' cannot be null.");
 		assertLong(2l).isGt(1);
-		assertThrown(()->assertLong(1l).isGt(2)).is("Value was not greater than expected.\n\tExpected=[2]\n\tActual=[1]");
-		assertThrown(()->assertLong(1l).isGt(1)).is("Value was not greater than expected.\n\tExpected=[1]\n\tActual=[1]");
+		assertThrown(()->assertLong(1l).isGt(2)).is("Value was not greater than expected.\n\tExpect=[2]\n\tActual=[1]");
+		assertThrown(()->assertLong(1l).isGt(1)).is("Value was not greater than expected.\n\tExpect=[1]\n\tActual=[1]");
 
 		assertThrown(()->assertLong(null).isGreaterThanOrEqual(1)).is("Value was null.");
 		assertThrown(()->assertLong(1l).isGreaterThanOrEqual(null)).is("Parameter 'value' cannot be null.");
 		assertLong(2l).isGreaterThanOrEqual(1);
-		assertThrown(()->assertLong(1l).isGreaterThanOrEqual(2)).is("Value was not greater than or equals to expected.\n\tExpected=[2]\n\tActual=[1]");
+		assertThrown(()->assertLong(1l).isGreaterThanOrEqual(2)).is("Value was not greater than or equals to expected.\n\tExpect=[2]\n\tActual=[1]");
 		assertLong(1l).isGreaterThanOrEqual(1);
 
 		assertThrown(()->assertLong(null).isGte(1)).is("Value was null.");
 		assertThrown(()->assertLong(1l).isGte(null)).is("Parameter 'value' cannot be null.");
 		assertLong(2l).isGte(1);
-		assertThrown(()->assertLong(1l).isGte(2l)).is("Value was not greater than or equals to expected.\n\tExpected=[2]\n\tActual=[1]");
+		assertThrown(()->assertLong(1l).isGte(2l)).is("Value was not greater than or equals to expected.\n\tExpect=[2]\n\tActual=[1]");
 		assertLong(1l).isGte(1l);
 
 		assertThrown(()->assertLong(null).isLessThan(1)).is("Value was null.");
 		assertThrown(()->assertLong(1l).isLessThan(null)).is("Parameter 'value' cannot be null.");
 		assertLong(1l).isLessThan(2l);
-		assertThrown(()->assertLong(2l).isLessThan(1)).is("Value was not less than expected.\n\tExpected=[1]\n\tActual=[2]");
-		assertThrown(()->assertLong(1l).isLessThan(1)).is("Value was not less than expected.\n\tExpected=[1]\n\tActual=[1]");
+		assertThrown(()->assertLong(2l).isLessThan(1)).is("Value was not less than expected.\n\tExpect=[1]\n\tActual=[2]");
+		assertThrown(()->assertLong(1l).isLessThan(1)).is("Value was not less than expected.\n\tExpect=[1]\n\tActual=[1]");
 
 		assertThrown(()->assertLong(null).isLt(1)).is("Value was null.");
 		assertThrown(()->assertLong(1l).isLt(null)).is("Parameter 'value' cannot be null.");
 		assertLong(1l).isLt(2);
-		assertThrown(()->assertLong(2l).isLt(1)).is("Value was not less than expected.\n\tExpected=[1]\n\tActual=[2]");
-		assertThrown(()->assertLong(1l).isLt(1)).is("Value was not less than expected.\n\tExpected=[1]\n\tActual=[1]");
+		assertThrown(()->assertLong(2l).isLt(1)).is("Value was not less than expected.\n\tExpect=[1]\n\tActual=[2]");
+		assertThrown(()->assertLong(1l).isLt(1)).is("Value was not less than expected.\n\tExpect=[1]\n\tActual=[1]");
 
 		assertThrown(()->assertLong(null).isLessThanOrEqual(1)).is("Value was null.");
 		assertThrown(()->assertLong(1l).isLessThanOrEqual(null)).is("Parameter 'value' cannot be null.");
 		assertLong(1l).isLessThanOrEqual(2);
-		assertThrown(()->assertLong(2l).isLessThanOrEqual(1)).is("Value was not less than or equals to expected.\n\tExpected=[1]\n\tActual=[2]");
+		assertThrown(()->assertLong(2l).isLessThanOrEqual(1)).is("Value was not less than or equals to expected.\n\tExpect=[1]\n\tActual=[2]");
 		assertLong(1l).isLessThanOrEqual(1);
 
 		assertThrown(()->assertLong(null).isLte(1)).is("Value was null.");
 		assertThrown(()->assertLong(1l).isLte(null)).is("Parameter 'value' cannot be null.");
 		assertLong(1l).isLte(2);
-		assertThrown(()->assertLong(2l).isLte(1)).is("Value was not less than or equals to expected.\n\tExpected=[1]\n\tActual=[2]");
+		assertThrown(()->assertLong(2l).isLte(1)).is("Value was not less than or equals to expected.\n\tExpect=[1]\n\tActual=[2]");
 		assertLong(1l).isLte(1);
 
 		assertThrown(()->assertLong(null).isBetween(1,3)).is("Value was null.");
@@ -92,8 +92,8 @@ public class FluentLongAssertion_Test {
 		assertLong(2l).isBetween(1,3);
 		assertLong(1l).isBetween(1,3);
 		assertLong(3l).isBetween(1,3);
-		assertThrown(()->assertLong(2l).isBetween(1,1)).is("Value was not less than or equals to expected.\n\tExpected=[1]\n\tActual=[2]");
-		assertThrown(()->assertLong(2l).isBetween(3,3)).is("Value was not greater than or equals to expected.\n\tExpected=[3]\n\tActual=[2]");
+		assertThrown(()->assertLong(2l).isBetween(1,1)).is("Value was not less than or equals to expected.\n\tExpect=[1]\n\tActual=[2]");
+		assertThrown(()->assertLong(2l).isBetween(3,3)).is("Value was not greater than or equals to expected.\n\tExpect=[3]\n\tActual=[2]");
 	}
 
 	@Test
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ListAssertion_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ListAssertion_Test.java
index 46ce35b..a96a5ce 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ListAssertion_Test.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ListAssertion_Test.java
@@ -35,9 +35,9 @@ public class ListAssertion_Test {
 
 		assertThrown(()->assertList(null).isSize(0)).is("Value was null.");
 		assertList(x1).isSize(0);
-		assertThrown(()->assertList(x1).isSize(1)).is("Collection did not have the expected size.  Expected=1, Actual=0.");
+		assertThrown(()->assertList(x1).isSize(1)).is("Collection did not have the expected size.  Expect=1, Actual=0.");
 		assertList(x2).isSize(2);
-		assertThrown(()->assertList(x2).isSize(0)).is("Collection did not have the expected size.  Expected=0, Actual=2.");
+		assertThrown(()->assertList(x2).isSize(0)).is("Collection did not have the expected size.  Expect=0, Actual=2.");
 
 		assertThrown(()->assertList(null).isEmpty()).is("Value was null.");
 		assertList(x1).isEmpty();
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ObjectAssertion_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ObjectAssertion_Test.java
index b88b37f..67141a4 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ObjectAssertion_Test.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ObjectAssertion_Test.java
@@ -48,7 +48,7 @@ public class ObjectAssertion_Test {
 		assertObject("foo").isType(String.class);
 		assertObject("foo").isType(CharSequence.class);
 		assertObject("foo").isType(Comparable.class);
-		assertThrown(()->assertObject(1).isType(String.class)).is("Unexpected class.\n\tExpected=[java.lang.String]\n\tActual=[java.lang.Integer]");
+		assertThrown(()->assertObject(1).isType(String.class)).is("Unexpected class.\n\tExpect=[java.lang.String]\n\tActual=[java.lang.Integer]");
 
 		assertObject("foo").serialized(JsonSerializer.DEFAULT).is("\"foo\"");
 		assertObject(null).serialized(JsonSerializer.DEFAULT).is("null");
@@ -62,17 +62,17 @@ public class ObjectAssertion_Test {
 
 		int[] x1 = {1,2}, x2 = {2,1};
 		assertObject(x2).jsonSorted().is("[1,2]");
-		assertThrown(()->assertObject(x2).jsonSorted().is("[2,1]")).stderr().is("Unexpected value.\n\tExpected=[[2,1]]\n\tActual=[[1,2]]");
+		assertThrown(()->assertObject(x2).jsonSorted().is("[2,1]")).stderr().is("Unexpected value.\n\tExpect=[[2,1]]\n\tActual=[[1,2]]");
 		assertObject(null).jsonSorted().is("null");
 
 		assertObject(x1).sameAs(x1);
-		assertThrown(()->assertObject(x1).sameAs(x2)).stderr().is("Unexpected comparison.\n\tExpected=[[2,1]]\n\tActual=[[1,2]]");
+		assertThrown(()->assertObject(x1).sameAs(x2)).stderr().is("Unexpected comparison.\n\tExpect=[[2,1]]\n\tActual=[[1,2]]");
 		assertObject(null).sameAs(null);
 		assertThrown(()->assertObject(new A1()).sameAs(null)).contains("Could not call getValue() on property 'foo'");
 
 		assertObject(x1).sameAsSorted(x1);
 		assertObject(x1).sameAsSorted(x2);
-		assertThrown(()->assertObject(x1).sameAs(null)).stderr().is("Unexpected comparison.\n\tExpected=[null]\n\tActual=[[1,2]]");
+		assertThrown(()->assertObject(x1).sameAs(null)).stderr().is("Unexpected comparison.\n\tExpect=[null]\n\tActual=[[1,2]]");
 		assertObject(null).sameAsSorted(null);
 
 		assertObject(x1).doesNotEqual(null);
@@ -90,7 +90,7 @@ public class ObjectAssertion_Test {
 		assertObject(x1).isNot(null);
 
 		assertObject(x1).isAny(x1,x2);
-		assertThrown(()->assertObject(x1).isAny(x2)).stderr().is("Expected value not found.\n\tExpected=[[[2,1]]]\n\tActual=[[1,2]]");
+		assertThrown(()->assertObject(x1).isAny(x2)).stderr().is("Expected value not found.\n\tExpect=[[[2,1]]]\n\tActual=[[1,2]]");
 
 		assertObject(x1).isNotAny(x2);
 		assertThrown(()->assertObject(x1).isNotAny(x1,x2)).stderr().is("Unexpected value found.\n\tUnexpected=[[1,2]]\n\tActual=[[1,2]]");
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/StringAssertion_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/StringAssertion_Test.java
index b5c428d..d022f26 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/StringAssertion_Test.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/StringAssertion_Test.java
@@ -49,30 +49,30 @@ public class StringAssertion_Test {
 
 		assertString("foo\nbar\nbaz").isEqualLines("foo","bar","baz");
 		assertThrown(()->assertString(null).isEqualLines((String[])null)).is("Parameter 'lines' cannot be null.");
-		assertThrown(()->assertString(null).isEqualLines((String)null)).is("Text differed at position -1.\n\tExpected=[]\n\tActual=[null]");
-		assertThrown(()->assertString("foo\nbar\nbaz").javaStrings().isEqualLines("foo","bar","bar")).stderr().is("Text differed at position 10.\n\tExpected=[foo\\nbar\\nbar]\n\tActual=[foo\\nbar\\nbaz]");
+		assertThrown(()->assertString(null).isEqualLines((String)null)).is("Text differed at position -1.\n\tExpect=[]\n\tActual=[null]");
+		assertThrown(()->assertString("foo\nbar\nbaz").javaStrings().isEqualLines("foo","bar","bar")).stderr().is("Text differed at position 10.\n\tExpect=[foo\\nbar\\nbar]\n\tActual=[foo\\nbar\\nbaz]");
 
 		assertString("foo\nbar\nbaz").isEqualSortedLines("bar","foo","baz");
 		assertThrown(()->assertString(null).isEqualSortedLines((String[])null)).is("Parameter 'lines' cannot be null.");
 		assertString("").isEqualSortedLines((String)null);
 		assertThrown(()->assertString(null).isEqualSortedLines()).is("Value was null.");
-		assertThrown(()->assertString("foo\nbar\nbaz").isEqualSortedLines("bar","foo","bar")).stderr().is("Expected text had different values at line 2.\n\tExpected=[bar]\n\tActual=[baz]");
-		assertThrown(()->assertString("foo\nbar\nbaz").isEqualSortedLines("bar","foo")).stderr().is("Expected text had different numbers of lines.\n\tExpected=[2]\n\tActual=[3]");
+		assertThrown(()->assertString("foo\nbar\nbaz").isEqualSortedLines("bar","foo","bar")).stderr().is("Expected text had different values at line 2.\n\tExpect=[bar]\n\tActual=[baz]");
+		assertThrown(()->assertString("foo\nbar\nbaz").isEqualSortedLines("bar","foo")).stderr().is("Expected text had different numbers of lines.\n\tExpect=[2]\n\tActual=[3]");
 		assertThrown(()->assertString(null).isEqualSortedLines("foo")).stderr().is("Value was null.");
-		assertThrown(()->assertString("foo").isEqualSortedLines((String)null)).stderr().is("Expected text had different values at line 1.\n\tExpected=[]\n\tActual=[foo]");
+		assertThrown(()->assertString("foo").isEqualSortedLines((String)null)).stderr().is("Expected text had different values at line 1.\n\tExpect=[]\n\tActual=[foo]");
 
 		assertString("foo\nbar\nbaz").isEqualLines("foo","bar","baz");
 
 		assertString("foobar").isEqualIc("FOOBAR");
 		assertString(null).isEqualIc(null);
-		assertThrown(()->assertString("foobar").isEqualIc("FOOBAZ")).stderr().is("Text differed at position 5.\n\tExpected=[FOOBAZ]\n\tActual=[foobar]");
-		assertThrown(()->assertString(null).isEqualIc("FOOBAZ")).stderr().is("Text differed at position 0.\n\tExpected=[FOOBAZ]\n\tActual=[null]");
-		assertThrown(()->assertString("foobar").isEqualIc(null)).stderr().is("Text differed at position 0.\n\tExpected=[null]\n\tActual=[foobar]");
+		assertThrown(()->assertString("foobar").isEqualIc("FOOBAZ")).stderr().is("Text differed at position 5.\n\tExpect=[FOOBAZ]\n\tActual=[foobar]");
+		assertThrown(()->assertString(null).isEqualIc("FOOBAZ")).stderr().is("Text differed at position 0.\n\tExpect=[FOOBAZ]\n\tActual=[null]");
+		assertThrown(()->assertString("foobar").isEqualIc(null)).stderr().is("Text differed at position 0.\n\tExpect=[null]\n\tActual=[foobar]");
 
 		assertString("foobar").doesNotEqual("foobaz");
 		assertThrown(()->assertString("foobar").doesNotEqual("foobar")).stderr().is("Text equaled unexpected.\n\tText=[foobar]");
 
-		assertThrown(()->assertString("foobar").isEqual("foobaz")).stderr().is("Text differed at position 5.\n\tExpected=[foobaz]\n\tActual=[foobar]");
+		assertThrown(()->assertString("foobar").isEqual("foobaz")).stderr().is("Text differed at position 5.\n\tExpect=[foobaz]\n\tActual=[foobar]");
 
 		assertString("foobar").isNot("foobaz");
 		assertThrown(()->assertString("foobar").isNot("foobar")).is("Text equaled unexpected.\n\tText=[foobar]");
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ThrowableAssertion_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ThrowableAssertion_Test.java
index f6420eb..39645aa 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ThrowableAssertion_Test.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/assertions/ThrowableAssertion_Test.java
@@ -27,8 +27,8 @@ public class ThrowableAssertion_Test {
 		Exception x1 = new RuntimeException("foo");
 
 		assertThrowable(x1).isType(Exception.class).isType(RuntimeException.class);
-		assertThrown(()->assertThrowable(x1).isType(IOException.class)).is("Exception was not expected type.\n\tExpected=[java.io.IOException]\n\tActual=[java.lang.RuntimeException]");
-		assertThrown(()->assertThrowable(null).isType(IOException.class)).is("Exception was not expected type.\n\tExpected=[java.io.IOException]\n\tActual=[null]");
+		assertThrown(()->assertThrowable(x1).isType(IOException.class)).is("Exception was not expected type.\n\tExpect=[java.io.IOException]\n\tActual=[java.lang.RuntimeException]");
+		assertThrown(()->assertThrowable(null).isType(IOException.class)).is("Exception was not expected type.\n\tExpect=[java.io.IOException]\n\tActual=[null]");
 		assertThrown(()->assertThrowable(x1).isType(null)).is("Parameter 'type' cannot be null.");
 
 		assertThrowable(x1).contains("foo");
@@ -50,7 +50,7 @@ public class ThrowableAssertion_Test {
 		assertThrown(()->assertThrowable(x1).passes(x->x.getMessage().equals("bar"))).is("Value did not pass predicate test.\n\tValue=[java.lang.RuntimeException: foo]");
 
 		assertThrowable(x1).passes(RuntimeException.class, x->x.getMessage().equals("foo"));
-		assertThrown(()->assertThrowable(x1).passes(IOException.class, x->x.getMessage().equals("foo"))).is("Exception was not expected type.\n\tExpected=[java.io.IOException]\n\tActual=[java.lang.RuntimeException]");
+		assertThrown(()->assertThrowable(x1).passes(IOException.class, x->x.getMessage().equals("foo"))).is("Exception was not expected type.\n\tExpect=[java.io.IOException]\n\tActual=[java.lang.RuntimeException]");
 		assertThrown(()->assertThrowable(x1).passes(RuntimeException.class, x->x.getMessage().equals("bar"))).is("Value did not pass predicate test.\n\tValue=[java.lang.RuntimeException: foo]");
 
 		assertThrowable(x1).message().is("foo");
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/CsvTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/Csv_Test.java
similarity index 96%
rename from juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/CsvTest.java
rename to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/Csv_Test.java
index 1a3b5de..4d8042c 100755
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/CsvTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/Csv_Test.java
@@ -21,7 +21,7 @@ import org.apache.juneau.serializer.*;
 import org.junit.*;
 
 @FixMethodOrder(NAME_ASCENDING)
-public class CsvTest {
+public class Csv_Test {
 
 	//====================================================================================================
 	// testBasic
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/annotation/CsvBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/annotation/CsvBuilder_Test.java
new file mode 100644
index 0000000..1257b11
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/annotation/CsvBuilder_Test.java
@@ -0,0 +1,114 @@
+// ***************************************************************************************************************************
+// * 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.csv.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class CsvBuilder_Test {
+
+	private static final String CNAME = CsvBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Csv a1 = CsvBuilder.create()
+		.on("a")
+		.build();
+
+	Csv a2 = CsvBuilder.create()
+		.on("a")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "on:['a'],"
+				+ "onClass:[]"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Csv c1 = CsvBuilder.create(C1.class).on(C2.class).build();
+		Csv c2 = CsvBuilder.create("a").on("b").build();
+		Csv c3 = CsvBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		Csv c4 = CsvBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Csv(
+		on="a"
+	)
+	public static class D1 {}
+	Csv d1 = D1.class.getAnnotationsByType(Csv.class)[0];
+
+	@Csv(
+		on="a"
+	)
+	public static class D2 {}
+	Csv d2 = D2.class.getAnnotationsByType(Csv.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/CsvConfigAnnotationTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/annotation/CsvConfig_Test.java
similarity index 96%
copy from juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/CsvConfigAnnotationTest.java
copy to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/annotation/CsvConfig_Test.java
index d9091fa..d577273 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/CsvConfigAnnotationTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/annotation/CsvConfig_Test.java
@@ -10,11 +10,11 @@
 // * "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.csv;
+package org.apache.juneau.csv.annotation;
 
 import static org.junit.runners.MethodSorters.*;
 
-import org.apache.juneau.csv.annotation.*;
+import org.apache.juneau.csv.*;
 import org.apache.juneau.reflect.*;
 import org.junit.*;
 
@@ -22,7 +22,7 @@ import org.junit.*;
  * Tests the @CsvConfig annotation.
  */
 @FixMethodOrder(NAME_ASCENDING)
-public class CsvConfigAnnotationTest {
+public class CsvConfig_Test {
 
 	//-----------------------------------------------------------------------------------------------------------------
 	// Annotation with no values.
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/BasicHtmlTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/BasicHtml_Test.java
similarity index 99%
rename from juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/BasicHtmlTest.java
rename to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/BasicHtml_Test.java
index 04e2f9e..72bdddd 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/BasicHtmlTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/BasicHtml_Test.java
@@ -31,7 +31,7 @@ import org.junit.runners.*;
 @RunWith(Parameterized.class)
 @SuppressWarnings({"serial","rawtypes","unchecked"})
 @FixMethodOrder(NAME_ASCENDING)
-public class BasicHtmlTest {
+public class BasicHtml_Test {
 
 	private static final Class<?>[] ANNOTATED_CLASSES = {
 		BeanWithWhitespaceTextFields2.class, BeanWithWhitespaceTextPwsFields2.class, BeanWithWhitespaceMixedFields2.class, BeanWithWhitespaceMixedPwsFields2.class, LinkBeanC.class
@@ -3032,7 +3032,7 @@ public class BasicHtmlTest {
 
 	private Input input;
 
-	public BasicHtmlTest(Input input) throws Exception {
+	public BasicHtml_Test(Input input) throws Exception {
 		this.input = input;
 	}
 
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/CommonParserTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/CommonParser_Test.java
similarity index 97%
rename from juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/CommonParserTest.java
rename to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/CommonParser_Test.java
index 65d0100..3537b10 100755
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/CommonParserTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/CommonParser_Test.java
@@ -25,7 +25,7 @@ import org.junit.*;
 
 @SuppressWarnings({"rawtypes","serial"})
 @FixMethodOrder(NAME_ASCENDING)
-public class CommonParserTest {
+public class CommonParser_Test {
 
 	//====================================================================================================
 	// testFromSerializer
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/CommonTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/Common_Test.java
similarity index 95%
rename from juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/CommonTest.java
rename to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/Common_Test.java
index 886bc58..620496c 100755
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/CommonTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/Common_Test.java
@@ -26,7 +26,7 @@ import org.junit.*;
 
 @SuppressWarnings({"serial"})
 @FixMethodOrder(NAME_ASCENDING)
-public class CommonTest {
+public class Common_Test {
 
 	//====================================================================================================
 	// Trim nulls from beans
@@ -360,7 +360,7 @@ public class CommonTest {
 
 		// Recursion detection, no ignore
 		s.detectRecursions();
-		assertThrown(()->s.build().serialize(r1)).contains("[0] <noname>:org.apache.juneau.html.CommonTest$R1", "->[1] r2:org.apache.juneau.html.CommonTest$R2", "->[2] r3:org.apache.juneau.html.CommonTest$R3", "->[3] r1:org.apache.juneau.html.CommonTest$R1");
+		assertThrown(()->s.build().serialize(r1)).contains("[0] <noname>:org.apache.juneau.html.Common_Test$R1", "->[1] r2:org.apache.juneau.html.Common_Test$R2", "->[2] r3:org.apache.juneau.html.Common_Test$R3", "->[3] r1:org.apache.juneau.html.Common_Test$R1");
 
 		s.ignoreRecursions();
 		assertEquals(
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlConfigAnnotationTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlConfigAnnotation_Test.java
similarity index 99%
rename from juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlConfigAnnotationTest.java
rename to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlConfigAnnotation_Test.java
index 769ec4f..b05766a 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlConfigAnnotationTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlConfigAnnotation_Test.java
@@ -27,7 +27,7 @@ import org.junit.*;
  * Tests the @HtmlConfig annotation.
  */
 @FixMethodOrder(NAME_ASCENDING)
-public class HtmlConfigAnnotationTest {
+public class HtmlConfigAnnotation_Test {
 
 	private static void check(String expected, Object o) {
 		assertEquals(expected, TO_STRING.apply(o));
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlDocConfigAnnotationTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlDocConfigAnnotation_Test.java
similarity index 99%
rename from juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlDocConfigAnnotationTest.java
rename to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlDocConfigAnnotation_Test.java
index d8b8729..53bf83a 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlDocConfigAnnotationTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlDocConfigAnnotation_Test.java
@@ -30,7 +30,7 @@ import org.junit.*;
  * Tests the @HtmlDocConfig annotation.
  */
 @FixMethodOrder(NAME_ASCENDING)
-public class HtmlDocConfigAnnotationTest {
+public class HtmlDocConfigAnnotation_Test {
 
 	private static void check(String expected, Object o) {
 		assertEquals(expected, TO_STRING.apply(o));
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/Html_Test.java
similarity index 96%
rename from juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlTest.java
rename to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/Html_Test.java
index cbfb14a..0ef6c02 100755
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/HtmlTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/Html_Test.java
@@ -27,7 +27,7 @@ import org.junit.*;
 
 @SuppressWarnings({"unchecked","rawtypes","serial"})
 @FixMethodOrder(NAME_ASCENDING)
-public class HtmlTest {
+public class Html_Test {
 
 	//-----------------------------------------------------------------------------------------------------------------
 	// Verifies that lists of maps/beans are converted to tables correctly.
@@ -355,7 +355,7 @@ public class HtmlTest {
 		assertEquals("<ul><li><table><tr><td>foo</td><td>bar</td></tr></table></li></ul>", r);
 	}
 
-	@HtmlConfig(applyHtml=@Html(on="org.apache.juneau.html.HtmlTest$MyMap2", noTables=true, noTableHeaders=true))
+	@HtmlConfig(applyHtml=@Html(on="org.apache.juneau.html.Html_Test$MyMap2", noTables=true, noTableHeaders=true))
 	public static class MyMap2 extends LinkedHashMap<String,String> {}
 
 	//-----------------------------------------------------------------------------------------------------------------
@@ -397,7 +397,7 @@ public class HtmlTest {
 
 	@Test
 	public void d05_testNoTableHeadersOnBeans_usingConcreteAnnotation() throws Exception {
-		HtmlSerializer s = HtmlSerializer.DEFAULT_SQ.builder().annotations(new HtmlAnnotation("MyBean2").noTables(true)).build();
+		HtmlSerializer s = HtmlSerializer.DEFAULT_SQ.builder().annotations(HtmlBuilder.create("MyBean2").noTables(true).build()).build();
 		Object o = null;
 		String r;
 
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/annotation/HtmlBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/annotation/HtmlBuilder_Test.java
new file mode 100644
index 0000000..0c86050
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/annotation/HtmlBuilder_Test.java
@@ -0,0 +1,146 @@
+// ***************************************************************************************************************************
+// * 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.html.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.html.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class HtmlBuilder_Test {
+
+	private static final String CNAME = HtmlBuilder_Test.class.getName();
+
+	private static class X1 extends HtmlRender<Object> {}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Html a1 = HtmlBuilder.create()
+		.anchorText("a")
+		.format(HtmlFormat.XML)
+		.link("c")
+		.noTableHeaders(true)
+		.noTables(true)
+		.on("d")
+		.render(X1.class)
+		.build();
+
+	Html a2 = HtmlBuilder.create()
+		.anchorText("a")
+		.format(HtmlFormat.XML)
+		.link("c")
+		.noTableHeaders(true)
+		.noTables(true)
+		.on("d")
+		.render(X1.class)
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).stderr().json().is(""
+			+ "{"
+				+ "anchorText:'a',"
+				+ "format:'XML',"
+				+ "link:'c',"
+				+ "noTableHeaders:true,"
+				+ "noTables:true,"
+				+ "on:['d'],"
+				+ "onClass:[],"
+				+ "render:'"+CNAME+"$X1'"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Html c1 = HtmlBuilder.create(C1.class).on(C2.class).build();
+		Html c2 = HtmlBuilder.create("a").on("b").build();
+		Html c3 = HtmlBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		Html c4 = HtmlBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Html(
+		anchorText="a",
+		format=HtmlFormat.XML,
+		link="c",
+		noTableHeaders=true,
+		noTables=true,
+		on="d",
+		render=X1.class
+	)
+	public static class D1 {}
+	Html d1 = D1.class.getAnnotationsByType(Html.class)[0];
+
+	@Html(
+		anchorText="a",
+		format=HtmlFormat.XML,
+		link="c",
+		noTableHeaders=true,
+		noTables=true,
+		on="d",
+		render=X1.class
+	)
+	public static class D2 {}
+	Html d2 = D2.class.getAnnotationsByType(Html.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/annotation/HtmlLinkBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/annotation/HtmlLinkBuilder_Test.java
new file mode 100644
index 0000000..bdc96ab
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/html/annotation/HtmlLinkBuilder_Test.java
@@ -0,0 +1,119 @@
+// ***************************************************************************************************************************
+// * 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.html.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class HtmlLinkBuilder_Test {
+
+	private static final String CNAME = HtmlLinkBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	HtmlLink a1 = HtmlLinkBuilder.create()
+		.nameProperty("a")
+		.on("b")
+		.uriProperty("c")
+		.build();
+
+	HtmlLink a2 = HtmlLinkBuilder.create()
+		.nameProperty("a")
+		.on("b")
+		.uriProperty("c")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "nameProperty:'a',"
+				+ "on:['b'],"
+				+ "onClass:[],"
+				+ "uriProperty:'c'"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		HtmlLink c1 = HtmlLinkBuilder.create(C1.class).on(C2.class).build();
+		HtmlLink c2 = HtmlLinkBuilder.create("a").on("b").build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@HtmlLink(
+		nameProperty="a",
+		on="b",
+		uriProperty="c"
+	)
+	public static class D1 {}
+	HtmlLink d1 = D1.class.getAnnotationsByType(HtmlLink.class)[0];
+
+	@HtmlLink(
+		nameProperty="a",
+		on="b",
+		uriProperty="c"
+	)
+	public static class D2 {}
+	HtmlLink d2 = D2.class.getAnnotationsByType(HtmlLink.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/AnnotationUtils_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/AnnotationUtils_Test.java
index 15bfdf7..f31a2eb 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/AnnotationUtils_Test.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/AnnotationUtils_Test.java
@@ -59,40 +59,40 @@ public class AnnotationUtils_Test {
 
 	@Test
 	public void a01_Body() throws Exception {
-		assertObject(body().annotationType()).json().contains("Body");
+		assertObject(body().build().annotationType()).json().contains("Body");
 
 		assertTrue(empty(A1.class.getAnnotation(Body.class)));
 		assertTrue(empty(A2.class.getAnnotation(Body.class)));
-		assertTrue(empty(body()));
+		assertTrue(empty(body().build()));
 		assertTrue(empty((Body)null));
 
-		assertFalse(empty(body().api(a("foo"))));
-		assertFalse(empty(body().d(a("foo"))));
-		assertFalse(empty(body().description(a("foo"))));
-		assertFalse(empty(body().ex(a("foo"))));
-		assertFalse(empty(body().example(a("foo"))));
-		assertFalse(empty(body().examples(a("foo"))));
-		assertFalse(empty(body().exs(a("foo"))));
-		assertFalse(empty(body().required(true)));
-		assertFalse(empty(body().r(true)));
-		assertFalse(empty(body().schema(schema().$ref("foo"))));
-		assertFalse(empty(body().value(a("foo"))));
+		assertFalse(empty(body().api(a("foo")).build()));
+		assertFalse(empty(body().d(a("foo")).build()));
+		assertFalse(empty(body().description(a("foo")).build()));
+		assertFalse(empty(body().ex(a("foo")).build()));
+		assertFalse(empty(body().example(a("foo")).build()));
+		assertFalse(empty(body().examples(a("foo")).build()));
+		assertFalse(empty(body().exs(a("foo")).build()));
+		assertFalse(empty(body().required(true).build()));
+		assertFalse(empty(body().r(true).build()));
+		assertFalse(empty(body().schema(schema().$ref("foo").build()).build()));
+		assertFalse(empty(body().value(a("foo")).build()));
 	}
 
 	@Test
 	public void a02_Contact() throws Exception {
 		X1 x1 = A1.class.getAnnotation(X1.class);
 
-		assertObject(contact().annotationType()).json().contains("Contact");
+		assertObject(contact().build().annotationType()).json().contains("Contact");
 
 		assertTrue(empty(x1.contact()));
-		assertTrue(empty(contact()));
+		assertTrue(empty(contact().build()));
 		assertTrue(empty((Contact)null));
 
-		assertFalse(empty(contact().email("foo")));
-		assertFalse(empty(contact().name("foo")));
-		assertFalse(empty(contact().url("foo")));
-		assertFalse(empty(contact().value(a("foo"))));
+		assertFalse(empty(contact().email("foo").build()));
+		assertFalse(empty(contact().name("foo").build()));
+		assertFalse(empty(contact().url("foo").build()));
+		assertFalse(empty(contact().value(a("foo")).build()));
 	}
 
 	@Test
@@ -100,72 +100,72 @@ public class AnnotationUtils_Test {
 		Field f1 = A1.class.getField("f1");
 		Field f2 = A2.class.getField("f1");
 
-		assertObject(formData().annotationType()).json().contains("FormData");
+		assertObject(formData().build().annotationType()).json().contains("FormData");
 
 		assertTrue(empty(f1.getAnnotation(FormData.class)));
 		assertTrue(empty(f2.getAnnotation(FormData.class)));
 		assertTrue(empty((FormData)null));
-		assertTrue(empty(formData()));
-
-		assertFalse(empty(formData()._default(a("foo"))));
-		assertFalse(empty(formData()._enum(a("foo"))));
-		assertFalse(empty(formData().aev(true)));
-		assertFalse(empty(formData().allowEmptyValue(true)));
-		assertFalse(empty(formData().api(a("foo"))));
-		assertFalse(empty(formData().cf("foo")));
-		assertFalse(empty(formData().collectionFormat("foo")));
-		assertFalse(empty(formData().d(a("foo"))));
-		assertFalse(empty(formData().description(a("foo"))));
-		assertFalse(empty(formData().df(a("foo"))));
-		assertFalse(empty(formData().e(a("foo"))));
-		assertFalse(empty(formData().emax(true)));
-		assertFalse(empty(formData().emin(true)));
-		assertFalse(empty(formData().ex(a("foo"))));
-		assertFalse(empty(formData().example(a("foo"))));
-		assertFalse(empty(formData().exclusiveMaximum(true)));
-		assertFalse(empty(formData().exclusiveMinimum(true)));
-		assertFalse(empty(formData().f("foo")));
-		assertFalse(empty(formData().format("foo")));
-		assertFalse(empty(formData().items(items().$ref("foo"))));
-		assertFalse(empty(formData().max("foo")));
-		assertFalse(empty(formData().maxi(0)));
-		assertFalse(empty(formData().maximum("foo")));
-		assertFalse(empty(formData().maxItems(0)));
-		assertFalse(empty(formData().maxl(0)));
-		assertFalse(empty(formData().maxLength(0)));
-		assertFalse(empty(formData().min("foo")));
-		assertFalse(empty(formData().mini(0)));
-		assertFalse(empty(formData().minimum("foo")));
-		assertFalse(empty(formData().minItems(0)));
-		assertFalse(empty(formData().minl(0)));
-		assertFalse(empty(formData().minLength(0)));
-		assertFalse(empty(formData().mo("foo")));
-		assertFalse(empty(formData().multi(true)));
-		assertFalse(empty(formData().multipleOf("foo")));
-		assertFalse(empty(formData().n("foo")));
-		assertFalse(empty(formData().name("foo")));
-		assertFalse(empty(formData().p("foo")));
-		assertFalse(empty(formData().parser(OpenApiParser.class)));
-		assertFalse(empty(formData().pattern("foo")));
-		assertFalse(empty(formData().r(true)));
-		assertFalse(empty(formData().required(true)));
-		assertFalse(empty(formData().serializer(OpenApiSerializer.class)));
-		assertFalse(empty(formData().sie(true)));
-		assertFalse(empty(formData().skipIfEmpty(true)));
-		assertFalse(empty(formData().t("foo")));
-		assertFalse(empty(formData().type("foo")));
-		assertFalse(empty(formData().ui(true)));
-		assertFalse(empty(formData().uniqueItems(true)));
-		assertFalse(empty(formData().value("foo")));
+		assertTrue(empty(formData().build()));
+
+		assertFalse(empty(formData()._default(a("foo")).build()));
+		assertFalse(empty(formData()._enum(a("foo")).build()));
+		assertFalse(empty(formData().aev(true).build()));
+		assertFalse(empty(formData().allowEmptyValue(true).build()));
+		assertFalse(empty(formData().api(a("foo")).build()));
+		assertFalse(empty(formData().cf("foo").build()));
+		assertFalse(empty(formData().collectionFormat("foo").build()));
+		assertFalse(empty(formData().d(a("foo")).build()));
+		assertFalse(empty(formData().description(a("foo")).build()));
+		assertFalse(empty(formData().df(a("foo")).build()));
+		assertFalse(empty(formData().e(a("foo")).build()));
+		assertFalse(empty(formData().emax(true).build()));
+		assertFalse(empty(formData().emin(true).build()));
+		assertFalse(empty(formData().ex(a("foo")).build()));
+		assertFalse(empty(formData().example(a("foo")).build()));
+		assertFalse(empty(formData().exclusiveMaximum(true).build()));
+		assertFalse(empty(formData().exclusiveMinimum(true).build()));
+		assertFalse(empty(formData().f("foo").build()));
+		assertFalse(empty(formData().format("foo").build()));
+		assertFalse(empty(formData().items(items().$ref("foo").build()).build()));
+		assertFalse(empty(formData().max("foo").build()));
+		assertFalse(empty(formData().maxi(0).build()));
+		assertFalse(empty(formData().maximum("foo").build()));
+		assertFalse(empty(formData().maxItems(0).build()));
+		assertFalse(empty(formData().maxl(0).build()));
+		assertFalse(empty(formData().maxLength(0).build()));
+		assertFalse(empty(formData().min("foo").build()));
+		assertFalse(empty(formData().mini(0).build()));
+		assertFalse(empty(formData().minimum("foo").build()));
+		assertFalse(empty(formData().minItems(0).build()));
+		assertFalse(empty(formData().minl(0).build()));
+		assertFalse(empty(formData().minLength(0).build()));
+		assertFalse(empty(formData().mo("foo").build()));
+		assertFalse(empty(formData().multi(true).build()));
+		assertFalse(empty(formData().multipleOf("foo").build()));
+		assertFalse(empty(formData().n("foo").build()));
+		assertFalse(empty(formData().name("foo").build()));
+		assertFalse(empty(formData().p("foo").build()));
+		assertFalse(empty(formData().parser(OpenApiParser.class).build()));
+		assertFalse(empty(formData().pattern("foo").build()));
+		assertFalse(empty(formData().r(true).build()));
+		assertFalse(empty(formData().required(true).build()));
+		assertFalse(empty(formData().serializer(OpenApiSerializer.class).build()));
+		assertFalse(empty(formData().sie(true).build()));
+		assertFalse(empty(formData().skipIfEmpty(true).build()));
+		assertFalse(empty(formData().t("foo").build()));
+		assertFalse(empty(formData().type("foo").build()));
+		assertFalse(empty(formData().ui(true).build()));
+		assertFalse(empty(formData().uniqueItems(true).build()));
+		assertFalse(empty(formData().value("foo").build()));
 	}
 
 	@Test
 	public void a04_HasFormData() throws Exception {
-		assertObject(hasFormData().annotationType()).json().contains("HasFormData");
+		assertObject(hasFormData().build().annotationType()).json().contains("HasFormData");
 
-		assertObject(hasFormData().n("foo").n()).json().is("'foo'");
-		assertObject(hasFormData().name("foo").name()).json().is("'foo'");
-		assertObject(hasFormData().value("foo").value()).json().is("'foo'");
+		assertObject(hasFormData().n("foo").build().n()).json().is("'foo'");
+		assertObject(hasFormData().name("foo").build().name()).json().is("'foo'");
+		assertObject(hasFormData().value("foo").build().value()).json().is("'foo'");
 	}
 
 	@Test
@@ -173,72 +173,72 @@ public class AnnotationUtils_Test {
 		Field f1 = A1.class.getField("f1");
 		Field f2 = A2.class.getField("f1");
 
-		assertObject(query().annotationType()).json().contains("Query");
+		assertObject(query().build().annotationType()).json().contains("Query");
 
 		assertTrue(empty(f1.getAnnotation(Query.class)));
 		assertTrue(empty(f2.getAnnotation(Query.class)));
 		assertTrue(empty((Query)null));
-		assertTrue(empty(query()));
-
-		assertFalse(empty(query()._default(a("foo"))));
-		assertFalse(empty(query()._enum(a("foo"))));
-		assertFalse(empty(query().aev(true)));
-		assertFalse(empty(query().allowEmptyValue(true)));
-		assertFalse(empty(query().api(a("foo"))));
-		assertFalse(empty(query().cf("foo")));
-		assertFalse(empty(query().collectionFormat("foo")));
-		assertFalse(empty(query().d(a("foo"))));
-		assertFalse(empty(query().description(a("foo"))));
-		assertFalse(empty(query().df(a("foo"))));
-		assertFalse(empty(query().e(a("foo"))));
-		assertFalse(empty(query().emax(true)));
-		assertFalse(empty(query().emin(true)));
-		assertFalse(empty(query().ex(a("foo"))));
-		assertFalse(empty(query().example(a("foo"))));
-		assertFalse(empty(query().exclusiveMaximum(true)));
-		assertFalse(empty(query().exclusiveMinimum(true)));
-		assertFalse(empty(query().f("foo")));
-		assertFalse(empty(query().format("foo")));
-		assertFalse(empty(query().items(items().$ref("foo"))));
-		assertFalse(empty(query().max("foo")));
-		assertFalse(empty(query().maxi(0)));
-		assertFalse(empty(query().maximum("foo")));
-		assertFalse(empty(query().maxItems(0)));
-		assertFalse(empty(query().maxl(0)));
-		assertFalse(empty(query().maxLength(0)));
-		assertFalse(empty(query().min("foo")));
-		assertFalse(empty(query().mini(0)));
-		assertFalse(empty(query().minimum("foo")));
-		assertFalse(empty(query().minItems(0)));
-		assertFalse(empty(query().minl(0)));
-		assertFalse(empty(query().minLength(0)));
-		assertFalse(empty(query().mo("foo")));
-		assertFalse(empty(query().multi(true)));
-		assertFalse(empty(query().multipleOf("foo")));
-		assertFalse(empty(query().n("foo")));
-		assertFalse(empty(query().name("foo")));
-		assertFalse(empty(query().p("foo")));
-		assertFalse(empty(query().parser(OpenApiParser.class)));
-		assertFalse(empty(query().pattern("foo")));
-		assertFalse(empty(query().r(true)));
-		assertFalse(empty(query().required(true)));
-		assertFalse(empty(query().serializer(OpenApiSerializer.class)));
-		assertFalse(empty(query().sie(true)));
-		assertFalse(empty(query().skipIfEmpty(true)));
-		assertFalse(empty(query().t("foo")));
-		assertFalse(empty(query().type("foo")));
-		assertFalse(empty(query().ui(true)));
-		assertFalse(empty(query().uniqueItems(true)));
-		assertFalse(empty(query().value("foo")));
+		assertTrue(empty(query().build()));
+
+		assertFalse(empty(query()._default(a("foo")).build()));
+		assertFalse(empty(query()._enum(a("foo")).build()));
+		assertFalse(empty(query().aev(true).build()));
+		assertFalse(empty(query().allowEmptyValue(true).build()));
+		assertFalse(empty(query().api(a("foo")).build()));
+		assertFalse(empty(query().cf("foo").build()));
+		assertFalse(empty(query().collectionFormat("foo").build()));
+		assertFalse(empty(query().d(a("foo")).build()));
+		assertFalse(empty(query().description(a("foo")).build()));
+		assertFalse(empty(query().df(a("foo")).build()));
+		assertFalse(empty(query().e(a("foo")).build()));
+		assertFalse(empty(query().emax(true).build()));
+		assertFalse(empty(query().emin(true).build()));
+		assertFalse(empty(query().ex(a("foo")).build()));
+		assertFalse(empty(query().example(a("foo")).build()));
+		assertFalse(empty(query().exclusiveMaximum(true).build()));
+		assertFalse(empty(query().exclusiveMinimum(true).build()));
+		assertFalse(empty(query().f("foo").build()));
+		assertFalse(empty(query().format("foo").build()));
+		assertFalse(empty(query().items(items().$ref("foo").build()).build()));
+		assertFalse(empty(query().max("foo").build()));
+		assertFalse(empty(query().maxi(0).build()));
+		assertFalse(empty(query().maximum("foo").build()));
+		assertFalse(empty(query().maxItems(0).build()));
+		assertFalse(empty(query().maxl(0).build()));
+		assertFalse(empty(query().maxLength(0).build()));
+		assertFalse(empty(query().min("foo").build()));
+		assertFalse(empty(query().mini(0).build()));
+		assertFalse(empty(query().minimum("foo").build()));
+		assertFalse(empty(query().minItems(0).build()));
+		assertFalse(empty(query().minl(0).build()));
+		assertFalse(empty(query().minLength(0).build()));
+		assertFalse(empty(query().mo("foo").build()));
+		assertFalse(empty(query().multi(true).build()));
+		assertFalse(empty(query().multipleOf("foo").build()));
+		assertFalse(empty(query().n("foo").build()));
+		assertFalse(empty(query().name("foo").build()));
+		assertFalse(empty(query().p("foo").build()));
+		assertFalse(empty(query().parser(OpenApiParser.class).build()));
+		assertFalse(empty(query().pattern("foo").build()));
+		assertFalse(empty(query().r(true).build()));
+		assertFalse(empty(query().required(true).build()));
+		assertFalse(empty(query().serializer(OpenApiSerializer.class).build()));
+		assertFalse(empty(query().sie(true).build()));
+		assertFalse(empty(query().skipIfEmpty(true).build()));
+		assertFalse(empty(query().t("foo").build()));
+		assertFalse(empty(query().type("foo").build()));
+		assertFalse(empty(query().ui(true).build()));
+		assertFalse(empty(query().uniqueItems(true).build()));
+		assertFalse(empty(query().value("foo").build()));
 	}
 
 	@Test
 	public void a06_HasQuery() throws Exception {
-		assertObject(hasQuery().annotationType()).json().contains("HasQuery");
+		assertObject(hasQuery().build().annotationType()).json().contains("HasQuery");
 
-		assertObject(hasQuery().n("foo").n()).json().is("'foo'");
-		assertObject(hasQuery().name("foo").name()).json().is("'foo'");
-		assertObject(hasQuery().value("foo").value()).json().is("'foo'");
+		assertObject(hasQuery().n("foo").build().n()).json().is("'foo'");
+		assertObject(hasQuery().name("foo").build().name()).json().is("'foo'");
+		assertObject(hasQuery().value("foo").build().value()).json().is("'foo'");
 	}
 
 	@Test
@@ -246,78 +246,78 @@ public class AnnotationUtils_Test {
 		Field f1 = A1.class.getField("f1");
 		Field f2 = A2.class.getField("f1");
 
-		assertObject(header().annotationType()).json().contains("Header");
+		assertObject(header().build().annotationType()).json().contains("Header");
 
 		assertTrue(empty(f1.getAnnotation(Header.class)));
 		assertTrue(empty(f2.getAnnotation(Header.class)));
 		assertTrue(empty((Header)null));
-		assertTrue(empty(header()));
-
-		assertFalse(empty(header()._default(a("foo"))));
-		assertFalse(empty(header()._enum(a("foo"))));
-		assertFalse(empty(header().aev(true)));
-		assertFalse(empty(header().allowEmptyValue(true)));
-		assertFalse(empty(header().api(a("foo"))));
-		assertFalse(empty(header().cf("foo")));
-		assertFalse(empty(header().collectionFormat("foo")));
-		assertFalse(empty(header().d(a("foo"))));
-		assertFalse(empty(header().description(a("foo"))));
-		assertFalse(empty(header().df(a("foo"))));
-		assertFalse(empty(header().e(a("foo"))));
-		assertFalse(empty(header().emax(true)));
-		assertFalse(empty(header().emin(true)));
-		assertFalse(empty(header().ex(a("foo"))));
-		assertFalse(empty(header().example(a("foo"))));
-		assertFalse(empty(header().exclusiveMaximum(true)));
-		assertFalse(empty(header().exclusiveMinimum(true)));
-		assertFalse(empty(header().f("foo")));
-		assertFalse(empty(header().format("foo")));
-		assertFalse(empty(header().items(items().$ref("foo"))));
-		assertFalse(empty(header().max("foo")));
-		assertFalse(empty(header().maxi(0)));
-		assertFalse(empty(header().maximum("foo")));
-		assertFalse(empty(header().maxItems(0)));
-		assertFalse(empty(header().maxl(0)));
-		assertFalse(empty(header().maxLength(0)));
-		assertFalse(empty(header().min("foo")));
-		assertFalse(empty(header().mini(0)));
-		assertFalse(empty(header().minimum("foo")));
-		assertFalse(empty(header().minItems(0)));
-		assertFalse(empty(header().minl(0)));
-		assertFalse(empty(header().minLength(0)));
-		assertFalse(empty(header().mo("foo")));
-		assertFalse(empty(header().multi(true)));
-		assertFalse(empty(header().multipleOf("foo")));
-		assertFalse(empty(header().n("foo")));
-		assertFalse(empty(header().name("foo")));
-		assertFalse(empty(header().p("foo")));
-		assertFalse(empty(header().parser(OpenApiParser.class)));
-		assertFalse(empty(header().pattern("foo")));
-		assertFalse(empty(header().r(true)));
-		assertFalse(empty(header().required(true)));
-		assertFalse(empty(header().serializer(OpenApiSerializer.class)));
-		assertFalse(empty(header().sie(true)));
-		assertFalse(empty(header().skipIfEmpty(true)));
-		assertFalse(empty(header().t("foo")));
-		assertFalse(empty(header().type("foo")));
-		assertFalse(empty(header().ui(true)));
-		assertFalse(empty(header().uniqueItems(true)));
-		assertFalse(empty(header().value("foo")));
+		assertTrue(empty(header().build()));
+
+		assertFalse(empty(header()._default(a("foo")).build()));
+		assertFalse(empty(header()._enum(a("foo")).build()));
+		assertFalse(empty(header().aev(true).build()));
+		assertFalse(empty(header().allowEmptyValue(true).build()));
+		assertFalse(empty(header().api(a("foo")).build()));
+		assertFalse(empty(header().cf("foo").build()));
+		assertFalse(empty(header().collectionFormat("foo").build()));
+		assertFalse(empty(header().d(a("foo")).build()));
+		assertFalse(empty(header().description(a("foo")).build()));
+		assertFalse(empty(header().df(a("foo")).build()));
+		assertFalse(empty(header().e(a("foo")).build()));
+		assertFalse(empty(header().emax(true).build()));
+		assertFalse(empty(header().emin(true).build()));
+		assertFalse(empty(header().ex(a("foo")).build()));
+		assertFalse(empty(header().example(a("foo")).build()));
+		assertFalse(empty(header().exclusiveMaximum(true).build()));
+		assertFalse(empty(header().exclusiveMinimum(true).build()));
+		assertFalse(empty(header().f("foo").build()));
+		assertFalse(empty(header().format("foo").build()));
+		assertFalse(empty(header().items(items().$ref("foo").build()).build()));
+		assertFalse(empty(header().max("foo").build()));
+		assertFalse(empty(header().maxi(0).build()));
+		assertFalse(empty(header().maximum("foo").build()));
+		assertFalse(empty(header().maxItems(0).build()));
+		assertFalse(empty(header().maxl(0).build()));
+		assertFalse(empty(header().maxLength(0).build()));
+		assertFalse(empty(header().min("foo").build()));
+		assertFalse(empty(header().mini(0).build()));
+		assertFalse(empty(header().minimum("foo").build()));
+		assertFalse(empty(header().minItems(0).build()));
+		assertFalse(empty(header().minl(0).build()));
+		assertFalse(empty(header().minLength(0).build()));
+		assertFalse(empty(header().mo("foo").build()));
+		assertFalse(empty(header().multi(true).build()));
+		assertFalse(empty(header().multipleOf("foo").build()));
+		assertFalse(empty(header().n("foo").build()));
+		assertFalse(empty(header().name("foo").build()));
+		assertFalse(empty(header().p("foo").build()));
+		assertFalse(empty(header().parser(OpenApiParser.class).build()));
+		assertFalse(empty(header().pattern("foo").build()));
+		assertFalse(empty(header().r(true).build()));
+		assertFalse(empty(header().required(true).build()));
+		assertFalse(empty(header().serializer(OpenApiSerializer.class).build()));
+		assertFalse(empty(header().sie(true).build()));
+		assertFalse(empty(header().skipIfEmpty(true).build()));
+		assertFalse(empty(header().t("foo").build()));
+		assertFalse(empty(header().type("foo").build()));
+		assertFalse(empty(header().ui(true).build()));
+		assertFalse(empty(header().uniqueItems(true).build()));
+		assertFalse(empty(header().value("foo").build()));
 	}
 
 	@Test
 	public void a08_License() throws Exception {
 		X1 x = A1.class.getAnnotation(X1.class);
 
-		assertObject(license().annotationType()).json().contains("License");
+		assertObject(license().build().annotationType()).json().contains("License");
 
 		assertTrue(empty(x.license()));
 		assertTrue(empty((License)null));
-		assertTrue(empty(license()));
+		assertTrue(empty(license().build()));
 
-		assertFalse(empty(license().name("foo")));
-		assertFalse(empty(license().url("foo")));
-		assertFalse(empty(license().value(a("foo"))));
+		assertFalse(empty(license().name("foo").build()));
+		assertFalse(empty(license().url("foo").build()));
+		assertFalse(empty(license().value(a("foo")).build()));
 	}
 
 	@Test
@@ -325,336 +325,335 @@ public class AnnotationUtils_Test {
 		Field f1 = A1.class.getField("f1");
 		Field f2 = A2.class.getField("f1");
 
-		assertObject(path().annotationType()).json().contains("Path");
+		assertObject(path().build().annotationType()).json().contains("Path");
 
 		assertTrue(empty(f1.getAnnotation(Path.class)));
 		assertTrue(empty(f2.getAnnotation(Path.class)));
 		assertTrue(empty((Path)null));
-		assertTrue(empty(path()));
-
-		assertFalse(empty(path()._enum(a("foo"))));
-		assertFalse(empty(path().aev(true)));
-		assertFalse(empty(path().allowEmptyValue(true)));
-		assertFalse(empty(path().api(a("foo"))));
-		assertFalse(empty(path().cf("foo")));
-		assertFalse(empty(path().collectionFormat("foo")));
-		assertFalse(empty(path().d(a("foo"))));
-		assertFalse(empty(path().description(a("foo"))));
-		assertFalse(empty(path().e(a("foo"))));
-		assertFalse(empty(path().emax(true)));
-		assertFalse(empty(path().emin(true)));
-		assertFalse(empty(path().ex(a("foo"))));
-		assertFalse(empty(path().example(a("foo"))));
-		assertFalse(empty(path().exclusiveMaximum(true)));
-		assertFalse(empty(path().exclusiveMinimum(true)));
-		assertFalse(empty(path().f("foo")));
-		assertFalse(empty(path().format("foo")));
-		assertFalse(empty(path().items(items().$ref("foo"))));
-		assertFalse(empty(path().max("foo")));
-		assertFalse(empty(path().maxi(0)));
-		assertFalse(empty(path().maximum("foo")));
-		assertFalse(empty(path().maxItems(0)));
-		assertFalse(empty(path().maxl(0)));
-		assertFalse(empty(path().maxLength(0)));
-		assertFalse(empty(path().min("foo")));
-		assertFalse(empty(path().mini(0)));
-		assertFalse(empty(path().minimum("foo")));
-		assertFalse(empty(path().minItems(0)));
-		assertFalse(empty(path().minl(0)));
-		assertFalse(empty(path().minLength(0)));
-		assertFalse(empty(path().mo("foo")));
-		assertFalse(empty(path().multipleOf("foo")));
-		assertFalse(empty(path().n("foo")));
-		assertFalse(empty(path().name("foo")));
-		assertFalse(empty(path().p("foo")));
-		assertFalse(empty(path().parser(OpenApiParser.class)));
-		assertFalse(empty(path().pattern("foo")));
-		assertFalse(empty(path().r(false)));
-		assertFalse(empty(path().required(false)));
-		assertFalse(empty(path().serializer(OpenApiSerializer.class)));
-		assertFalse(empty(path().t("foo")));
-		assertFalse(empty(path().type("foo")));
-		assertFalse(empty(path().ui(true)));
-		assertFalse(empty(path().uniqueItems(true)));
-		assertFalse(empty(path().value("foo")));
+		assertTrue(empty(path().build()));
+
+		assertFalse(empty(path()._enum(a("foo")).build()));
+		assertFalse(empty(path().aev(true).build()));
+		assertFalse(empty(path().allowEmptyValue(true).build()));
+		assertFalse(empty(path().api(a("foo")).build()));
+		assertFalse(empty(path().cf("foo").build()));
+		assertFalse(empty(path().collectionFormat("foo").build()));
+		assertFalse(empty(path().d(a("foo")).build()));
+		assertFalse(empty(path().description(a("foo")).build()));
+		assertFalse(empty(path().e(a("foo")).build()));
+		assertFalse(empty(path().emax(true).build()));
+		assertFalse(empty(path().emin(true).build()));
+		assertFalse(empty(path().ex(a("foo")).build()));
+		assertFalse(empty(path().example(a("foo")).build()));
+		assertFalse(empty(path().exclusiveMaximum(true).build()));
+		assertFalse(empty(path().exclusiveMinimum(true).build()));
+		assertFalse(empty(path().f("foo").build()));
+		assertFalse(empty(path().format("foo").build()));
+		assertFalse(empty(path().items(items().$ref("foo").build()).build()));
+		assertFalse(empty(path().max("foo").build()));
+		assertFalse(empty(path().maxi(0).build()));
+		assertFalse(empty(path().maximum("foo").build()));
+		assertFalse(empty(path().maxItems(0).build()));
+		assertFalse(empty(path().maxl(0).build()));
+		assertFalse(empty(path().maxLength(0).build()));
+		assertFalse(empty(path().min("foo").build()));
+		assertFalse(empty(path().mini(0).build()));
+		assertFalse(empty(path().minimum("foo").build()));
+		assertFalse(empty(path().minItems(0).build()));
+		assertFalse(empty(path().minl(0).build()));
+		assertFalse(empty(path().minLength(0).build()));
+		assertFalse(empty(path().mo("foo").build()));
+		assertFalse(empty(path().multipleOf("foo").build()));
+		assertFalse(empty(path().n("foo").build()));
+		assertFalse(empty(path().name("foo").build()));
+		assertFalse(empty(path().p("foo").build()));
+		assertFalse(empty(path().parser(OpenApiParser.class).build()));
+		assertFalse(empty(path().pattern("foo").build()));
+		assertFalse(empty(path().r(false).build()));
+		assertFalse(empty(path().required(false).build()));
+		assertFalse(empty(path().serializer(OpenApiSerializer.class).build()));
+		assertFalse(empty(path().t("foo").build()));
+		assertFalse(empty(path().type("foo").build()));
+		assertFalse(empty(path().ui(true).build()));
+		assertFalse(empty(path().uniqueItems(true).build()));
+		assertFalse(empty(path().value("foo").build()));
 	}
 
 	@Test
 	public void a10_Request() throws Exception {
-		assertObject(request().annotationType()).json().contains("Request");
+		assertObject(request().build().annotationType()).json().contains("Request");
 
-		assertObject(request().parser(OpenApiParser.class).parser()).json().is("'org.apache.juneau.oapi.OpenApiParser'");
-		assertObject(request().serializer(OpenApiSerializer.class).serializer()).json().is("'org.apache.juneau.oapi.OpenApiSerializer'");
+		assertObject(request().parser(OpenApiParser.class).build().parser()).json().is("'org.apache.juneau.oapi.OpenApiParser'");
+		assertObject(request().serializer(OpenApiSerializer.class).build().serializer()).json().is("'org.apache.juneau.oapi.OpenApiSerializer'");
 	}
 
 	@Test
 	public void a11_Response() throws Exception {
-		assertObject(response().annotationType()).json().contains("Response");
+		assertObject(response().build().annotationType()).json().contains("Response");
 
 		assertTrue(empty(A1.class.getAnnotation(Response.class)));
 		assertTrue(empty(A2.class.getAnnotation(Response.class)));
-		assertTrue(empty(response()));
+		assertTrue(empty(response().build()));
 		assertTrue(empty((Response)null));
 
-		assertFalse(empty(response().api(a("foo"))));
-		assertFalse(empty(response().code(a(0))));
-		assertFalse(empty(response().d(a("foo"))));
-		assertFalse(empty(response().description(a("foo"))));
-		assertFalse(empty(response().ex(a("foo"))));
-		assertFalse(empty(response().example(a("foo"))));
-		assertFalse(empty(response().examples(a("foo"))));
-		assertFalse(empty(response().exs(a("foo"))));
-		assertFalse(empty(response().headers(new ResponseHeader[]{responseHeader().$ref("foo")})));
-		assertFalse(empty(response().parser(OpenApiParser.class)));
-		assertFalse(empty(response().schema(schema().$ref("foo"))));
-		assertFalse(empty(response().serializer(OpenApiSerializer.class)));
-		assertFalse(empty(response().value(a(0))));
+		assertFalse(empty(response().api(a("foo")).build()));
+		assertFalse(empty(response().code(a(0)).build()));
+		assertFalse(empty(response().d(a("foo")).build()));
+		assertFalse(empty(response().description(a("foo")).build()));
+		assertFalse(empty(response().ex(a("foo")).build()));
+		assertFalse(empty(response().example(a("foo")).build()));
+		assertFalse(empty(response().examples(a("foo")).build()));
+		assertFalse(empty(response().exs(a("foo")).build()));
+		assertFalse(empty(response().headers(new ResponseHeader[]{responseHeader().$ref("foo").build()}).build()));
+		assertFalse(empty(response().parser(OpenApiParser.class).build()));
+		assertFalse(empty(response().schema(schema().$ref("foo").build()).build()));
+		assertFalse(empty(response().serializer(OpenApiSerializer.class).build()));
+		assertFalse(empty(response().value(a(0)).build()));
 	}
 
 	@Test
 	public void a12_ResponseBody() throws Exception {
-		assertObject(responseBody().annotationType()).json().contains("ResponseBody");
+		assertObject(responseBody().build().annotationType()).json().contains("ResponseBody");
 	}
 
 	@Test
 	public void a13_ResponseHeader() throws Exception {
-		assertObject(responseHeader().annotationType()).json().contains("ResponseHeader");
+		assertObject(responseHeader().build().annotationType()).json().contains("ResponseHeader");
 
 		assertTrue(empty(A1.class.getAnnotation(ResponseHeader.class)));
 		assertTrue(empty(A2.class.getAnnotation(ResponseHeader.class)));
 
-		assertFalse(empty(responseHeader()._default(a("foo"))));
-		assertFalse(empty(responseHeader()._enum(a("foo"))));
-		assertFalse(empty(responseHeader().api(a("foo"))));
-		assertFalse(empty(responseHeader().code(a(0))));
-		assertFalse(empty(responseHeader().cf("foo")));
-		assertFalse(empty(responseHeader().collectionFormat("foo")));
-		assertFalse(empty(responseHeader().d(a("foo"))));
-		assertFalse(empty(responseHeader().description(a("foo"))));
-		assertFalse(empty(responseHeader().df(a("foo"))));
-		assertFalse(empty(responseHeader().e(a("foo"))));
-		assertFalse(empty(responseHeader().emax(true)));
-		assertFalse(empty(responseHeader().emin(true)));
-		assertFalse(empty(responseHeader().ex(a("foo"))));
-		assertFalse(empty(responseHeader().example(a("foo"))));
-		assertFalse(empty(responseHeader().exclusiveMaximum(true)));
-		assertFalse(empty(responseHeader().exclusiveMinimum(true)));
-		assertFalse(empty(responseHeader().f("foo")));
-		assertFalse(empty(responseHeader().format("foo")));
-		assertFalse(empty(responseHeader().items(items().$ref("foo"))));
-		assertFalse(empty(responseHeader().max("foo")));
-		assertFalse(empty(responseHeader().maxi(0)));
-		assertFalse(empty(responseHeader().maximum("foo")));
-		assertFalse(empty(responseHeader().maxItems(0)));
-		assertFalse(empty(responseHeader().maxl(0)));
-		assertFalse(empty(responseHeader().maxLength(0)));
-		assertFalse(empty(responseHeader().min("foo")));
-		assertFalse(empty(responseHeader().mini(0)));
-		assertFalse(empty(responseHeader().minimum("foo")));
-		assertFalse(empty(responseHeader().minItems(0)));
-		assertFalse(empty(responseHeader().minl(0)));
-		assertFalse(empty(responseHeader().minLength(0)));
-		assertFalse(empty(responseHeader().mo("foo")));
-		assertFalse(empty(responseHeader().multipleOf("foo")));
-		assertFalse(empty(responseHeader().n("foo")));
-		assertFalse(empty(responseHeader().name("foo")));
-		assertFalse(empty(responseHeader().p("foo")));
-		assertFalse(empty(responseHeader().pattern("foo")));
-		assertFalse(empty(responseHeader().serializer(OpenApiSerializer.class)));
-		assertFalse(empty(responseHeader().t("foo")));
-		assertFalse(empty(responseHeader().type("foo")));
-		assertFalse(empty(responseHeader().ui(true)));
-		assertFalse(empty(responseHeader().uniqueItems(true)));
-		assertFalse(empty(responseHeader().value("foo")));
+		assertFalse(empty(responseHeader()._default(a("foo")).build()));
+		assertFalse(empty(responseHeader()._enum(a("foo")).build()));
+		assertFalse(empty(responseHeader().api(a("foo")).build()));
+		assertFalse(empty(responseHeader().code(a(0)).build()));
+		assertFalse(empty(responseHeader().cf("foo").build()));
+		assertFalse(empty(responseHeader().collectionFormat("foo").build()));
+		assertFalse(empty(responseHeader().d(a("foo")).build()));
+		assertFalse(empty(responseHeader().description(a("foo")).build()));
+		assertFalse(empty(responseHeader().df(a("foo")).build()));
+		assertFalse(empty(responseHeader().e(a("foo")).build()));
+		assertFalse(empty(responseHeader().emax(true).build()));
+		assertFalse(empty(responseHeader().emin(true).build()));
+		assertFalse(empty(responseHeader().ex(a("foo")).build()));
+		assertFalse(empty(responseHeader().example(a("foo")).build()));
+		assertFalse(empty(responseHeader().exclusiveMaximum(true).build()));
+		assertFalse(empty(responseHeader().exclusiveMinimum(true).build()));
+		assertFalse(empty(responseHeader().f("foo").build()));
+		assertFalse(empty(responseHeader().format("foo").build()));
+		assertFalse(empty(responseHeader().items(items().$ref("foo").build()).build()));
+		assertFalse(empty(responseHeader().max("foo").build()));
+		assertFalse(empty(responseHeader().maxi(0).build()));
+		assertFalse(empty(responseHeader().maximum("foo").build()));
+		assertFalse(empty(responseHeader().maxItems(0).build()));
+		assertFalse(empty(responseHeader().maxl(0).build()));
+		assertFalse(empty(responseHeader().maxLength(0).build()));
+		assertFalse(empty(responseHeader().min("foo").build()));
+		assertFalse(empty(responseHeader().mini(0).build()));
+		assertFalse(empty(responseHeader().minimum("foo").build()));
+		assertFalse(empty(responseHeader().minItems(0).build()));
+		assertFalse(empty(responseHeader().minl(0).build()));
+		assertFalse(empty(responseHeader().minLength(0).build()));
+		assertFalse(empty(responseHeader().mo("foo").build()));
+		assertFalse(empty(responseHeader().multipleOf("foo").build()));
+		assertFalse(empty(responseHeader().n("foo").build()));
+		assertFalse(empty(responseHeader().name("foo").build()));
+		assertFalse(empty(responseHeader().p("foo").build()));
+		assertFalse(empty(responseHeader().pattern("foo").build()));
+		assertFalse(empty(responseHeader().serializer(OpenApiSerializer.class).build()));
+		assertFalse(empty(responseHeader().t("foo").build()));
+		assertFalse(empty(responseHeader().type("foo").build()));
+		assertFalse(empty(responseHeader().ui(true).build()));
+		assertFalse(empty(responseHeader().uniqueItems(true).build()));
+		assertFalse(empty(responseHeader().value("foo").build()));
 	}
 
 	@Test
 	public void a14_ResponseStatus() throws Exception {
-		assertObject(responseStatus().annotationType()).json().contains("ResponseStatus");
+		assertObject(responseStatus().build().annotationType()).json().contains("ResponseStatus");
 	}
 
 	@Test
 	public void a15_Tag() throws Exception {
-		assertObject(tag().annotationType()).json().contains("Tag");
+		assertObject(tag().build().annotationType()).json().contains("Tag");
 
-		assertObject(tag().description(a("foo")).description()).json().is("['foo']");
-		assertObject(tag().externalDocs(externalDocs().url("foo")).externalDocs().url()).json().is("'foo'");
-		assertObject(tag().name("foo").name()).json().is("'foo'");
-		assertObject(tag().value(a("foo")).value()).json().is("['foo']");
+		assertObject(tag().description(a("foo")).build().description()).json().is("['foo']");
+		assertObject(tag().externalDocs(externalDocs().url("foo").build()).build().externalDocs().url()).json().is("'foo'");
+		assertObject(tag().name("foo").build().name()).json().is("'foo'");
+		assertObject(tag().value(a("foo")).build().value()).json().is("['foo']");
 	}
 
 	@Test
 	public void a16_ExternalDocs() throws Exception {
 		X1 x = A1.class.getAnnotation(X1.class);
 
-		assertObject(externalDocs().annotationType()).json().contains("ExternalDocs");
+		assertObject(externalDocs().build().annotationType()).json().contains("ExternalDocs");
 
 		assertTrue(empty(x.externalDocs()));
 		assertTrue(empty((ExternalDocs)null));
 
-		assertFalse(empty(externalDocs().description(a("foo"))));
-		assertFalse(empty(externalDocs().url("foo")));
-		assertFalse(empty(externalDocs().value(a("foo"))));
+		assertFalse(empty(externalDocs().description(a("foo")).build()));
+		assertFalse(empty(externalDocs().url("foo").build()));
+		assertFalse(empty(externalDocs().value(a("foo")).build()));
 	}
 
 	@Test
 	public void a17_Schema() throws Exception {
 		X1 x = A1.class.getAnnotation(X1.class);
 
-		assertObject(schema().annotationType()).json().contains("Schema");
+		assertObject(schema().build().annotationType()).json().contains("Schema");
 
 		assertTrue(empty(x.schema()));
 		assertTrue(empty((Schema)null));
 
-		assertFalse(empty(schema()._default(a("foo"))));
-		assertFalse(empty(schema()._enum(a("foo"))));
-		assertFalse(empty(schema().$ref("foo")));
-		assertFalse(empty(schema().additionalProperties(a("foo"))));
-		assertFalse(empty(schema().allOf(a("foo"))));
-		assertFalse(empty(schema().cf("foo")));
-		assertFalse(empty(schema().collectionFormat("foo")));
-		assertFalse(empty(schema().d(a("foo"))));
-		assertFalse(empty(schema().description(a("foo"))));
-		assertFalse(empty(schema().df(a("foo"))));
-		assertFalse(empty(schema().discriminator("foo")));
-		assertFalse(empty(schema().e(a("foo"))));
-		assertFalse(empty(schema().emax(true)));
-		assertFalse(empty(schema().emin(true)));
-		assertFalse(empty(schema().ex(a("foo"))));
-		assertFalse(empty(schema().example(a("foo"))));
-		assertFalse(empty(schema().examples(a("foo"))));
-		assertFalse(empty(schema().exclusiveMaximum(true)));
-		assertFalse(empty(schema().exclusiveMinimum(true)));
-		assertFalse(empty(schema().exs(a("foo"))));
-		assertFalse(empty(schema().externalDocs(externalDocs().url("foo"))));
-		assertFalse(empty(schema().f("foo")));
-		assertFalse(empty(schema().format("foo")));
-		assertFalse(empty(schema().ignore(true)));
-		assertFalse(empty(schema().items(items().$ref("foo"))));
-		assertFalse(empty(schema().max("foo")));
-		assertFalse(empty(schema().maxi(0)));
-		assertFalse(empty(schema().maximum("foo")));
-		assertFalse(empty(schema().maxItems(0)));
-		assertFalse(empty(schema().maxl(0)));
-		assertFalse(empty(schema().maxLength(0)));
-		assertFalse(empty(schema().maxp(0)));
-		assertFalse(empty(schema().maxProperties(0)));
-		assertFalse(empty(schema().min("foo")));
-		assertFalse(empty(schema().mini(0)));
-		assertFalse(empty(schema().minimum("foo")));
-		assertFalse(empty(schema().minItems(0)));
-		assertFalse(empty(schema().minl(0)));
-		assertFalse(empty(schema().minLength(0)));
-		assertFalse(empty(schema().minp(0)));
-		assertFalse(empty(schema().minProperties(0)));
-		assertFalse(empty(schema().mo("foo")));
-		assertFalse(empty(schema().multipleOf("foo")));
-		assertFalse(empty(schema().on("foo")));
-		assertFalse(empty(schema().p("foo")));
-		assertFalse(empty(schema().pattern("foo")));
-		assertFalse(empty(schema().properties(a("foo"))));
-		assertFalse(empty(schema().r(true)));
-		assertFalse(empty(schema().readOnly(true)));
-		assertFalse(empty(schema().required(true)));
-		assertFalse(empty(schema().ro(true)));
-		assertFalse(empty(schema().t("foo")));
-		assertFalse(empty(schema().title("foo")));
-		assertFalse(empty(schema().type("foo")));
-		assertFalse(empty(schema().ui(true)));
-		assertFalse(empty(schema().uniqueItems(true)));
-		assertFalse(empty(schema().value(a("foo"))));
-		assertFalse(empty(schema().xml(a("foo"))));
+		assertFalse(empty(schema()._default(a("foo")).build()));
+		assertFalse(empty(schema()._enum(a("foo")).build()));
+		assertFalse(empty(schema().$ref("foo").build()));
+		assertFalse(empty(schema().additionalProperties(a("foo")).build()));
+		assertFalse(empty(schema().allOf(a("foo")).build()));
+		assertFalse(empty(schema().cf("foo").build()));
+		assertFalse(empty(schema().collectionFormat("foo").build()));
+		assertFalse(empty(schema().d(a("foo")).build()));
+		assertFalse(empty(schema().description(a("foo")).build()));
+		assertFalse(empty(schema().df(a("foo")).build()));
+		assertFalse(empty(schema().discriminator("foo").build()));
+		assertFalse(empty(schema().e(a("foo")).build()));
+		assertFalse(empty(schema().emax(true).build()));
+		assertFalse(empty(schema().emin(true).build()));
+		assertFalse(empty(schema().ex(a("foo")).build()));
+		assertFalse(empty(schema().example(a("foo")).build()));
+		assertFalse(empty(schema().examples(a("foo")).build()));
+		assertFalse(empty(schema().exclusiveMaximum(true).build()));
+		assertFalse(empty(schema().exclusiveMinimum(true).build()));
+		assertFalse(empty(schema().exs(a("foo")).build()));
+		assertFalse(empty(schema().externalDocs(externalDocs().url("foo").build()).build()));
+		assertFalse(empty(schema().f("foo").build()));
+		assertFalse(empty(schema().format("foo").build()));
+		assertFalse(empty(schema().ignore(true).build()));
+		assertFalse(empty(schema().items(items().$ref("foo").build()).build()));
+		assertFalse(empty(schema().max("foo").build()));
+		assertFalse(empty(schema().maxi(0).build()));
+		assertFalse(empty(schema().maximum("foo").build()));
+		assertFalse(empty(schema().maxItems(0).build()));
+		assertFalse(empty(schema().maxl(0).build()));
+		assertFalse(empty(schema().maxLength(0).build()));
+		assertFalse(empty(schema().maxp(0).build()));
+		assertFalse(empty(schema().maxProperties(0).build()));
+		assertFalse(empty(schema().min("foo").build()));
+		assertFalse(empty(schema().mini(0).build()));
+		assertFalse(empty(schema().minimum("foo").build()));
+		assertFalse(empty(schema().minItems(0).build()));
+		assertFalse(empty(schema().minl(0).build()));
+		assertFalse(empty(schema().minLength(0).build()));
+		assertFalse(empty(schema().minp(0).build()));
+		assertFalse(empty(schema().minProperties(0).build()));
+		assertFalse(empty(schema().mo("foo").build()));
+		assertFalse(empty(schema().multipleOf("foo").build()));
+		assertFalse(empty(schema().p("foo").build()));
+		assertFalse(empty(schema().pattern("foo").build()));
+		assertFalse(empty(schema().properties(a("foo")).build()));
+		assertFalse(empty(schema().r(true).build()));
+		assertFalse(empty(schema().readOnly(true).build()));
+		assertFalse(empty(schema().required(true).build()));
+		assertFalse(empty(schema().ro(true).build()));
+		assertFalse(empty(schema().t("foo").build()));
+		assertFalse(empty(schema().title("foo").build()));
+		assertFalse(empty(schema().type("foo").build()));
+		assertFalse(empty(schema().ui(true).build()));
+		assertFalse(empty(schema().uniqueItems(true).build()));
+		assertFalse(empty(schema().value(a("foo")).build()));
+		assertFalse(empty(schema().xml(a("foo")).build()));
 	}
 
 	@Test
 	public void a18_SubItems() throws Exception {
 		X1 x = A1.class.getAnnotation(X1.class);
 
-		assertObject(subItems().annotationType()).json().contains("SubItems");
+		assertObject(subItems().build().annotationType()).json().contains("SubItems");
 
 		assertTrue(empty(x.subItems()));
 		assertTrue(empty((SubItems)null));
 
-		assertFalse(empty(subItems()._default(a("foo"))));
-		assertFalse(empty(subItems()._enum(a("foo"))));
-		assertFalse(empty(subItems().$ref("foo")));
-		assertFalse(empty(subItems().cf("foo")));
-		assertFalse(empty(subItems().collectionFormat("foo")));
-		assertFalse(empty(subItems().df(a("foo"))));
-		assertFalse(empty(subItems().e(a("foo"))));
-		assertFalse(empty(subItems().emax(true)));
-		assertFalse(empty(subItems().emin(true)));
-		assertFalse(empty(subItems().exclusiveMaximum(true)));
-		assertFalse(empty(subItems().exclusiveMinimum(true)));
-		assertFalse(empty(subItems().f("foo")));
-		assertFalse(empty(subItems().format("foo")));
-		assertFalse(empty(subItems().items(a("foo"))));
-		assertFalse(empty(subItems().max("foo")));
-		assertFalse(empty(subItems().maxi(0)));
-		assertFalse(empty(subItems().maximum("foo")));
-		assertFalse(empty(subItems().maxItems(0)));
-		assertFalse(empty(subItems().maxl(0)));
-		assertFalse(empty(subItems().maxLength(0)));
-		assertFalse(empty(subItems().min("foo")));
-		assertFalse(empty(subItems().mini(0)));
-		assertFalse(empty(subItems().minimum("foo")));
-		assertFalse(empty(subItems().minItems(0)));
-		assertFalse(empty(subItems().minl(0)));
-		assertFalse(empty(subItems().minLength(0)));
-		assertFalse(empty(subItems().mo("foo")));
-		assertFalse(empty(subItems().multipleOf("foo")));
-		assertFalse(empty(subItems().p("foo")));
-		assertFalse(empty(subItems().pattern("foo")));
-		assertFalse(empty(subItems().t("foo")));
-		assertFalse(empty(subItems().type("foo")));
-		assertFalse(empty(subItems().ui(true)));
-		assertFalse(empty(subItems().uniqueItems(true)));
-		assertFalse(empty(subItems().value(a("foo"))));
+		assertFalse(empty(subItems()._default(a("foo")).build()));
+		assertFalse(empty(subItems()._enum(a("foo")).build()));
+		assertFalse(empty(subItems().$ref("foo").build()));
+		assertFalse(empty(subItems().cf("foo").build()));
+		assertFalse(empty(subItems().collectionFormat("foo").build()));
+		assertFalse(empty(subItems().df(a("foo")).build()));
+		assertFalse(empty(subItems().e(a("foo")).build()));
+		assertFalse(empty(subItems().emax(true).build()));
+		assertFalse(empty(subItems().emin(true).build()));
+		assertFalse(empty(subItems().exclusiveMaximum(true).build()));
+		assertFalse(empty(subItems().exclusiveMinimum(true).build()));
+		assertFalse(empty(subItems().f("foo").build()));
+		assertFalse(empty(subItems().format("foo").build()));
+		assertFalse(empty(subItems().items(a("foo")).build()));
+		assertFalse(empty(subItems().max("foo").build()));
+		assertFalse(empty(subItems().maxi(0).build()));
+		assertFalse(empty(subItems().maximum("foo").build()));
+		assertFalse(empty(subItems().maxItems(0).build()));
+		assertFalse(empty(subItems().maxl(0).build()));
+		assertFalse(empty(subItems().maxLength(0).build()));
+		assertFalse(empty(subItems().min("foo").build()));
+		assertFalse(empty(subItems().mini(0).build()));
+		assertFalse(empty(subItems().minimum("foo").build()));
+		assertFalse(empty(subItems().minItems(0).build()));
+		assertFalse(empty(subItems().minl(0).build()));
+		assertFalse(empty(subItems().minLength(0).build()));
+		assertFalse(empty(subItems().mo("foo").build()));
+		assertFalse(empty(subItems().multipleOf("foo").build()));
+		assertFalse(empty(subItems().p("foo").build()));
+		assertFalse(empty(subItems().pattern("foo").build()));
+		assertFalse(empty(subItems().t("foo").build()));
+		assertFalse(empty(subItems().type("foo").build()));
+		assertFalse(empty(subItems().ui(true).build()));
+		assertFalse(empty(subItems().uniqueItems(true).build()));
+		assertFalse(empty(subItems().value(a("foo")).build()));
 	}
 
 	@Test
 	public void a19_Items() throws Exception {
 		X1 x = A1.class.getAnnotation(X1.class);
 
-		assertObject(items().annotationType()).json().contains("Items");
+		assertObject(items().build().annotationType()).json().contains("Items");
 
 		assertTrue(empty(x.items()));
 		assertTrue(empty((Items)null));
-		assertFalse(empty(items()._default(a("foo"))));
-		assertFalse(empty(items()._enum(a("foo"))));
-		assertFalse(empty(items().$ref("foo")));
-		assertFalse(empty(items().cf("foo")));
-		assertFalse(empty(items().collectionFormat("foo")));
-		assertFalse(empty(items().df(a("foo"))));
-		assertFalse(empty(items().e(a("foo"))));
-		assertFalse(empty(items().emax(true)));
-		assertFalse(empty(items().emin(true)));
-		assertFalse(empty(items().exclusiveMaximum(true)));
-		assertFalse(empty(items().exclusiveMinimum(true)));
-		assertFalse(empty(items().f("foo")));
-		assertFalse(empty(items().format("foo")));
-		assertFalse(empty(items().items(subItems().$ref("foo"))));
-		assertFalse(empty(items().max("foo")));
-		assertFalse(empty(items().maxi(0)));
-		assertFalse(empty(items().maximum("foo")));
-		assertFalse(empty(items().maxItems(0)));
-		assertFalse(empty(items().maxl(0)));
-		assertFalse(empty(items().maxLength(0)));
-		assertFalse(empty(items().min("foo")));
-		assertFalse(empty(items().mini(0)));
-		assertFalse(empty(items().minimum("foo")));
-		assertFalse(empty(items().minItems(0)));
-		assertFalse(empty(items().minl(0)));
-		assertFalse(empty(items().minLength(0)));
-		assertFalse(empty(items().mo("foo")));
-		assertFalse(empty(items().multipleOf("foo")));
-		assertFalse(empty(items().p("foo")));
-		assertFalse(empty(items().pattern("foo")));
-		assertFalse(empty(items().t("foo")));
-		assertFalse(empty(items().type("foo")));
-		assertFalse(empty(items().ui(true)));
-		assertFalse(empty(items().uniqueItems(true)));
-		assertFalse(empty(items().value(a("foo"))));
+		assertFalse(empty(items()._default(a("foo")).build()));
+		assertFalse(empty(items()._enum(a("foo")).build()));
+		assertFalse(empty(items().$ref("foo").build()));
+		assertFalse(empty(items().cf("foo").build()));
+		assertFalse(empty(items().collectionFormat("foo").build()));
+		assertFalse(empty(items().df(a("foo")).build()));
+		assertFalse(empty(items().e(a("foo")).build()));
+		assertFalse(empty(items().emax(true).build()));
+		assertFalse(empty(items().emin(true).build()));
+		assertFalse(empty(items().exclusiveMaximum(true).build()));
+		assertFalse(empty(items().exclusiveMinimum(true).build()));
+		assertFalse(empty(items().f("foo").build()));
+		assertFalse(empty(items().format("foo").build()));
+		assertFalse(empty(items().items(subItems().$ref("foo").build()).build()));
+		assertFalse(empty(items().max("foo").build()));
+		assertFalse(empty(items().maxi(0).build()));
+		assertFalse(empty(items().maximum("foo").build()));
+		assertFalse(empty(items().maxItems(0).build()));
+		assertFalse(empty(items().maxl(0).build()));
+		assertFalse(empty(items().maxLength(0).build()));
+		assertFalse(empty(items().min("foo").build()));
+		assertFalse(empty(items().mini(0).build()));
+		assertFalse(empty(items().minimum("foo").build()));
+		assertFalse(empty(items().minItems(0).build()));
+		assertFalse(empty(items().minl(0).build()));
+		assertFalse(empty(items().minLength(0).build()));
+		assertFalse(empty(items().mo("foo").build()));
+		assertFalse(empty(items().multipleOf("foo").build()));
+		assertFalse(empty(items().p("foo").build()));
+		assertFalse(empty(items().pattern("foo").build()));
+		assertFalse(empty(items().t("foo").build()));
+		assertFalse(empty(items().type("foo").build()));
+		assertFalse(empty(items().ui(true).build()));
+		assertFalse(empty(items().uniqueItems(true).build()));
+		assertFalse(empty(items().value(a("foo")).build()));
 	}
 
 
@@ -714,80 +713,80 @@ public class AnnotationUtils_Test {
 	// Helper methods.
 	//-----------------------------------------------------------------------------------------------------------------
 
-	private static BodyAnnotation body() {
-		return new BodyAnnotation();
+	private static BodyBuilder body() {
+		return BodyBuilder.create();
 	}
 
-	private static ContactAnnotation contact() {
-		return new ContactAnnotation();
+	private static ContactBuilder contact() {
+		return ContactBuilder.create();
 	}
 
-	private static FormDataAnnotation formData() {
-		return new FormDataAnnotation();
+	private static FormDataBuilder formData() {
+		return FormDataBuilder.create();
 	}
 
-	private static HasFormDataAnnotation hasFormData() {
-		return new HasFormDataAnnotation();
+	private static HasFormDataBuilder hasFormData() {
+		return HasFormDataBuilder.create();
 	}
 
-	private static QueryAnnotation query() {
-		return new QueryAnnotation();
+	private static QueryBuilder query() {
+		return QueryBuilder.create();
 	}
 
-	private static HasQueryAnnotation hasQuery() {
-		return new HasQueryAnnotation();
+	private static HasQueryBuilder hasQuery() {
+		return HasQueryBuilder.create();
 	}
 
-	private static HeaderAnnotation header() {
-		return new HeaderAnnotation();
+	private static HeaderBuilder header() {
+		return HeaderBuilder.create();
 	}
 
-	private static LicenseAnnotation license() {
-		return new LicenseAnnotation();
+	private static LicenseBuilder license() {
+		return LicenseBuilder.create();
 	}
 
-	private static PathAnnotation path() {
-		return new PathAnnotation();
+	private static PathBuilder path() {
+		return PathBuilder.create();
 	}
 
-	private static RequestAnnotation request() {
-		return new RequestAnnotation();
+	private static RequestBuilder request() {
+		return RequestBuilder.create();
 	}
 
-	private static ResponseAnnotation response() {
-		return new ResponseAnnotation();
+	private static ResponseBuilder response() {
+		return ResponseBuilder.create();
 	}
 
-	private static ResponseBodyAnnotation responseBody() {
-		return new ResponseBodyAnnotation();
+	private static ResponseBodyBuilder responseBody() {
+		return ResponseBodyBuilder.create();
 	}
 
-	private static ResponseHeaderAnnotation responseHeader() {
-		return new ResponseHeaderAnnotation();
+	private static ResponseHeaderBuilder responseHeader() {
+		return ResponseHeaderBuilder.create();
 	}
 
-	private static ResponseStatusAnnotation responseStatus() {
-		return new ResponseStatusAnnotation();
+	private static ResponseStatusBuilder responseStatus() {
+		return ResponseStatusBuilder.create();
 	}
 
-	private static TagAnnotation tag() {
-		return new TagAnnotation();
+	private static TagBuilder tag() {
+		return TagBuilder.create();
 	}
 
-	private static SchemaAnnotation schema() {
-		return new SchemaAnnotation();
+	private static SchemaBuilder schema() {
+		return SchemaBuilder.create();
 	}
 
-	private static ItemsAnnotation items() {
-		return new ItemsAnnotation();
+	private static ItemsBuilder items() {
+		return ItemsBuilder.create();
 	}
 
-	private static SubItemsAnnotation subItems() {
-		return new SubItemsAnnotation();
+	private static SubItemsBuilder subItems() {
+		return SubItemsBuilder.create();
 	}
 
-	private static ExternalDocsAnnotation externalDocs() {
-		return new ExternalDocsAnnotation();
+	private static ExternalDocsBuilder externalDocs() {
+		return ExternalDocsBuilder.create();
 	}
 
 	private static String[] a(String...s) {
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/BodyBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/BodyBuilder_Test.java
new file mode 100644
index 0000000..f059798
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/BodyBuilder_Test.java
@@ -0,0 +1,173 @@
+// ***************************************************************************************************************************
+// * 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.http.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.jsonschema.annotation.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class BodyBuilder_Test {
+
+	private static final String CNAME = BodyBuilder_Test.class.getName();
+
+	public static class X1 {}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Body a1 = BodyBuilder.create()
+		.api("api")
+		.d("d")
+		.description("description")
+		.ex("ex")
+		.example("example")
+		.examples("examples")
+		.exs("exs")
+		.on("on")
+		.onClass(X1.class)
+		.r(true)
+		.required(true)
+		.schema(SchemaBuilder.create().build())
+		.value("value")
+		.build();
+
+	Body a2 = BodyBuilder.create()
+		.api("api")
+		.d("d")
+		.description("description")
+		.ex("ex")
+		.example("example")
+		.examples("examples")
+		.exs("exs")
+		.on("on")
+		.onClass(X1.class)
+		.r(true)
+		.required(true)
+		.schema(SchemaBuilder.create().build())
+		.value("value")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "api:['api'],"
+				+ "d:['d'],"
+				+ "description:['description'],"
+				+ "ex:['ex'],"
+				+ "example:['example'],"
+				+ "examples:['examples'],"
+				+ "exs:['exs'],"
+				+ "on:['on'],"
+				+ "onClass:['"+CNAME+"$X1'],"
+				+ "r:true,"
+				+ "required:true,"
+				+ "schema:{'$ref':'',_default:[],_enum:[],additionalProperties:[],allOf:[],cf:'',collectionFormat:'',d:[],description:[],df:[],discriminator:'',e:[],emax:false,emin:false,ex:[],example:[],examples:[],exclusiveMaximum:false,exclusiveMinimum:false,exs:[],externalDocs:{description:[],url:'',value:[]},f:'',format:'',ignore:false,items:{'$ref':'',_default:[],_enum:[],cf:'',collectionFormat:'',df:[],e:[],emax:false,emin:false,exclusiveMaximum:false,exclusiveMinimum:false,f:'',format:'',ite [...]
+				+ "value:['value']"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Body c1 = BodyBuilder.create(C1.class).on(C2.class).build();
+		Body c2 = BodyBuilder.create("a").on("b").build();
+		Body c4 = BodyBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Body(
+		api="api",
+		d="d",
+		description="description",
+		ex="ex",
+		example="example",
+		examples="examples",
+		exs="exs",
+		on="on",
+		onClass=X1.class,
+		r=true,
+		required=true,
+		schema=@Schema,
+		value="value"
+	)
+	public static class D1 {}
+	Body d1 = D1.class.getAnnotationsByType(Body.class)[0];
+
+	@Body(
+		api="api",
+		d="d",
+		description="description",
+		ex="ex",
+		example="example",
+		examples="examples",
+		exs="exs",
+		on="on",
+		onClass=X1.class,
+		r=true,
+		required=true,
+		schema=@Schema,
+		value="value"
+	)
+	public static class D2 {}
+	Body d2 = D2.class.getAnnotationsByType(Body.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/ContactBuilder_Test.java
similarity index 53%
copy from juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreTest.java
copy to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/ContactBuilder_Test.java
index fe34da0..0584731 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/ContactBuilder_Test.java
@@ -1,125 +1,99 @@
-// ***************************************************************************************************************************
-// * 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.annotation;
-
-import static org.apache.juneau.assertions.Assertions.*;
-import static org.junit.runners.MethodSorters.*;
-
-import org.apache.juneau.json.*;
-import org.junit.*;
-
-@FixMethodOrder(NAME_ASCENDING)
-public class BeanIgnoreTest {
-
-	//------------------------------------------------------------------------------------------------------------------
-	// Test @BeanIgnore on properties
-	//------------------------------------------------------------------------------------------------------------------
-
-	public static class A {
-		public String getA() {
-			return "a";
-		}
-
-		@BeanIgnore
-		public String getB() {
-			return "b";
-		}
-
-		public String c = "c";
-
-		@BeanIgnore public String d = "d";
-	}
-
-	@Test
-	public void testBeanIgnoreOnProperties() throws Exception {
-		assertObject(new A()).json().is("{c:'c',a:'a'}");
-	}
-
-	@BeanConfig(
-		applyBeanIgnore={
-			@BeanIgnore(on="Ac.getB"),
-			@BeanIgnore(on="Ac.d")
-		}
-	)
-	public static class Ac {
-		public String getA() {
-			return "a";
-		}
-
-		public String getB() {
-			return "b";
-		}
-
-		public String c = "c";
-
-		public String d = "d";
-	}
-
-	@Test
-	public void testBeanIgnoreOnProperties_usingConfig() throws Exception {
-		assertObject(new Ac()).serialized(SimpleJsonSerializer.DEFAULT.builder().applyAnnotations(Ac.class).build()).is("{c:'c',a:'a'}");
-	}
-
-	//------------------------------------------------------------------------------------------------------------------
-	// Test @BeanIgnore on classes
-	//------------------------------------------------------------------------------------------------------------------
-
-	@BeanIgnore
-	public static class B1 {
-		public int f = 1;
-
-		@Override
-		public String toString() {
-			return "xxx";
-		}
-	}
-
-	public static class B {
-		public int f2 = 2;
-		public B1 f3 = new B1();
-
-		public B1 getF4() {
-			return new B1();
-		}
-	}
-
-	@Test
-	public void testBeanIgnoreOnBean() throws Exception {
-		assertObject(new B()).json().is("{f2:2,f3:'xxx',f4:'xxx'}");
-	}
-
-	@BeanConfig(applyBeanIgnore=@BeanIgnore(on="B1c"))
-	public static class B1c {
-		public int f = 1;
-
-		@Override
-		public String toString() {
-			return "xxx";
-		}
-	}
-
-	public static class Bc {
-		public int f2 = 2;
-		public B1c f3 = new B1c();
-
-		public B1c getF4() {
-			return new B1c();
-		}
-	}
-
-	@Test
-	public void testBeanIgnoreOnBean_usingConfig() throws Exception {
-		assertObject(new Bc()).serialized(SimpleJsonSerializer.DEFAULT.builder().applyAnnotations(B1c.class).build()).is("{f2:2,f3:'xxx',f4:'xxx'}");
-	}
-}
-
+// ***************************************************************************************************************************
+// * 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.http.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class ContactBuilder_Test {
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Contact a1 = ContactBuilder.create()
+		.email("email")
+		.name("name")
+		.url("url")
+		.value("value")
+		.build();
+
+	Contact a2 = ContactBuilder.create()
+		.email("email")
+		.name("name")
+		.url("url")
+		.value("value")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "email:'email',"
+				+ "name:'name',"
+				+ "url:'url',"
+				+ "value:['value']"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Contact(
+		email="email",
+		name="name",
+		url="url",
+		value="value"
+	)
+	public static class D1 {}
+	Contact d1 = D1.class.getAnnotationsByType(Contact.class)[0];
+
+	@Contact(
+		email="email",
+		name="name",
+		url="url",
+		value="value"
+	)
+	public static class D2 {}
+	Contact d2 = D2.class.getAnnotationsByType(Contact.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/FormDataBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/FormDataBuilder_Test.java
new file mode 100644
index 0000000..cf0a585
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/FormDataBuilder_Test.java
@@ -0,0 +1,371 @@
+// ***************************************************************************************************************************
+// * 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.http.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.jsonschema.annotation.*;
+import org.apache.juneau.oapi.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class FormDataBuilder_Test {
+
+	private static final String CNAME = FormDataBuilder_Test.class.getName();
+
+	public static class X1 {}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	FormData a1 = FormDataBuilder.create()
+		._default("default")
+		._enum("enum")
+		.aev(true)
+		.allowEmptyValue(true)
+		.api("api")
+		.cf("cf")
+		.collectionFormat("collectionFormat")
+		.d("d")
+		.description("description")
+		.df("df")
+		.e("e")
+		.emax(true)
+		.emin(true)
+		.ex("ex")
+		.example("example")
+		.exclusiveMaximum(true)
+		.exclusiveMinimum(true)
+		.f("f")
+		.format("format")
+		.items(ItemsBuilder.DEFAULT)
+		.max("max")
+		.maxi(1)
+		.maximum("maximum")
+		.maxItems(2)
+		.maxl(3)
+		.maxLength(4)
+		.min("min")
+		.mini(5)
+		.minimum("minimum")
+		.minItems(6)
+		.minl(7)
+		.minLength(8)
+		.mo("mo")
+		.multi(true)
+		.multipleOf("multipleOf")
+		.n("n")
+		.name("name")
+		.on("on")
+		.onClass(X1.class)
+		.p("p")
+		.parser(OpenApiParser.class)
+		.pattern("pattern")
+		.r(true)
+		.required(true)
+		.serializer(OpenApiSerializer.class)
+		.sie(true)
+		.skipIfEmpty(true)
+		.t("t")
+		.type("type")
+		.ui(true)
+		.uniqueItems(true)
+		.value("value")
+		.build();
+
+	FormData a2 = FormDataBuilder.create()
+		._default("default")
+		._enum("enum")
+		.aev(true)
+		.allowEmptyValue(true)
+		.api("api")
+		.cf("cf")
+		.collectionFormat("collectionFormat")
+		.d("d")
+		.description("description")
+		.df("df")
+		.e("e")
+		.emax(true)
+		.emin(true)
+		.ex("ex")
+		.example("example")
+		.exclusiveMaximum(true)
+		.exclusiveMinimum(true)
+		.f("f")
+		.format("format")
+		.items(ItemsBuilder.DEFAULT)
+		.max("max")
+		.maxi(1)
+		.maximum("maximum")
+		.maxItems(2)
+		.maxl(3)
+		.maxLength(4)
+		.min("min")
+		.mini(5)
+		.minimum("minimum")
+		.minItems(6)
+		.minl(7)
+		.minLength(8)
+		.mo("mo")
+		.multi(true)
+		.multipleOf("multipleOf")
+		.n("n")
+		.name("name")
+		.on("on")
+		.onClass(X1.class)
+		.p("p")
+		.parser(OpenApiParser.class)
+		.pattern("pattern")
+		.r(true)
+		.required(true)
+		.serializer(OpenApiSerializer.class)
+		.sie(true)
+		.skipIfEmpty(true)
+		.t("t")
+		.type("type")
+		.ui(true)
+		.uniqueItems(true)
+		.value("value")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "_default:['default'],"
+				+ "_enum:['enum'],"
+				+ "aev:true,"
+				+ "allowEmptyValue:true,"
+				+ "api:['api'],"
+				+ "cf:'cf',"
+				+ "collectionFormat:'collectionFormat',"
+				+ "d:['d'],"
+				+ "description:['description'],"
+				+ "df:['df'],"
+				+ "e:['e'],"
+				+ "emax:true,"
+				+ "emin:true,"
+				+ "ex:['ex'],"
+				+ "example:['example'],"
+				+ "exclusiveMaximum:true,"
+				+ "exclusiveMinimum:true,"
+				+ "f:'f',"
+				+ "format:'format',"
+				+ "items:{'$ref':'',_default:[],_enum:[],cf:'',collectionFormat:'',df:[],e:[],emax:false,emin:false,exclusiveMaximum:false,exclusiveMinimum:false,f:'',format:'',items:{'$ref':'',_default:[],_enum:[],cf:'',collectionFormat:'',df:[],e:[],emax:false,emin:false,exclusiveMaximum:false,exclusiveMinimum:false,f:'',format:'',items:[],max:'',maxItems:-1,maxLength:-1,maxi:-1,maximum:'',maxl:-1,min:'',minItems:-1,minLength:-1,mini:-1,minimum:'',minl:-1,mo:'',multipleOf:'',p:'',pattern:'',t:'',t [...]
+				+ "max:'max',"
+				+ "maxItems:2,"
+				+ "maxLength:4,"
+				+ "maxi:1,"
+				+ "maximum:'maximum',"
+				+ "maxl:3,"
+				+ "min:'min',"
+				+ "minItems:6,"
+				+ "minLength:8,"
+				+ "mini:5,"
+				+ "minimum:'minimum',"
+				+ "minl:7,"
+				+ "mo:'mo',"
+				+ "multi:true,"
+				+ "multipleOf:'multipleOf',"
+				+ "n:'n',"
+				+ "name:'name',"
+				+ "on:['on'],"
+				+ "onClass:['org.apache.juneau.http.annotation.FormDataBuilder_Test$X1'],"
+				+ "p:'p',"
+				+ "parser:'org.apache.juneau.oapi.OpenApiParser',"
+				+ "pattern:'pattern',"
+				+ "r:true,"
+				+ "required:true,"
+				+ "serializer:'org.apache.juneau.oapi.OpenApiSerializer',"
+				+ "sie:true,"
+				+ "skipIfEmpty:true,"
+				+ "t:'t',"
+				+ "type:'type',"
+				+ "ui:true,"
+				+ "uniqueItems:true,"
+				+ "value:'value'"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		FormData c1 = FormDataBuilder.create(C1.class).on(C2.class).build();
+		FormData c2 = FormDataBuilder.create("a").on("b").build();
+		FormData c3 = FormDataBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		FormData c4 = FormDataBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@FormData(
+		_default="default",
+		_enum="enum",
+		aev=true,
+		allowEmptyValue=true,
+		api="api",
+		cf="cf",
+		collectionFormat="collectionFormat",
+		d="d",
+		description="description",
+		df="df",
+		e="e",
+		emax=true,
+		emin=true,
+		ex="ex",
+		example="example",
+		exclusiveMaximum=true,
+		exclusiveMinimum=true,
+		f="f",
+		format="format",
+		items=@Items,
+		max="max",
+		maxi=1,
+		maximum="maximum",
+		maxItems=2,
+		maxl=3,
+		maxLength=4,
+		min="min",
+		mini=5,
+		minimum="minimum",
+		minItems=6,
+		minl=7,
+		minLength=8,
+		mo="mo",
+		multi=true,
+		multipleOf="multipleOf",
+		n="n",
+		name="name",
+		on="on",
+		onClass=X1.class,
+		p="p",
+		parser=OpenApiParser.class,
+		pattern="pattern",
+		r=true,
+		required=true,
+		serializer=OpenApiSerializer.class,
+		sie=true,
+		skipIfEmpty=true,
+		t="t",
+		type="type",
+		ui=true,
+		uniqueItems=true,
+		value="value"
+	)
+	public static class D1 {}
+	FormData d1 = D1.class.getAnnotationsByType(FormData.class)[0];
+
+	@FormData(
+		_default="default",
+		_enum="enum",
+		aev=true,
+		allowEmptyValue=true,
+		api="api",
+		cf="cf",
+		collectionFormat="collectionFormat",
+		d="d",
+		description="description",
+		df="df",
+		e="e",
+		emax=true,
+		emin=true,
+		ex="ex",
+		example="example",
+		exclusiveMaximum=true,
+		exclusiveMinimum=true,
+		f="f",
+		format="format",
+		items=@Items,
+		max="max",
+		maxi=1,
+		maximum="maximum",
+		maxItems=2,
+		maxl=3,
+		maxLength=4,
+		min="min",
+		mini=5,
+		minimum="minimum",
+		minItems=6,
+		minl=7,
+		minLength=8,
+		mo="mo",
+		multi=true,
+		multipleOf="multipleOf",
+		n="n",
+		name="name",
+		on="on",
+		onClass=X1.class,
+		p="p",
+		parser=OpenApiParser.class,
+		pattern="pattern",
+		r=true,
+		required=true,
+		serializer=OpenApiSerializer.class,
+		sie=true,
+		skipIfEmpty=true,
+		t="t",
+		type="type",
+		ui=true,
+		uniqueItems=true,
+		value="value"
+	)
+	public static class D2 {}
+	FormData d2 = D2.class.getAnnotationsByType(FormData.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/CsvConfigAnnotationTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/HasFormDataBuilder_Test.java
similarity index 60%
copy from juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/CsvConfigAnnotationTest.java
copy to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/HasFormDataBuilder_Test.java
index d9091fa..2807b9b 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/CsvConfigAnnotationTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/HasFormDataBuilder_Test.java
@@ -10,56 +10,60 @@
 // * "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.csv;
+package org.apache.juneau.http.annotation;
 
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
 import static org.junit.runners.MethodSorters.*;
 
-import org.apache.juneau.csv.annotation.*;
-import org.apache.juneau.reflect.*;
+import org.apache.juneau.*;
 import org.junit.*;
 
-/**
- * Tests the @CsvConfig annotation.
- */
 @FixMethodOrder(NAME_ASCENDING)
-public class CsvConfigAnnotationTest {
+public class HasFormDataBuilder_Test {
 
-	//-----------------------------------------------------------------------------------------------------------------
-	// Annotation with no values.
-	//-----------------------------------------------------------------------------------------------------------------
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
 
-	@CsvConfig()
-	static class B {}
-	static ClassInfo b = ClassInfo.of(B.class);
+	HasFormData a1 = HasFormDataBuilder.create()
+		.n("n")
+		.name("name")
+		.value("value")
+		.build();
+
+	HasFormData a2 = HasFormDataBuilder.create()
+		.n("n")
+		.name("name")
+		.value("value")
+		.build();
 
 	@Test
-	public void defaultsSerializer() throws Exception {
-		AnnotationList al = b.getAnnotationList();
-		CsvSerializer.create().applyAnnotations(al, null).build();
+	public void a01_basic() {
+		assertObject(a1).stderr().json().is(""
+			+ "{"
+				+ "n:'n',"
+				+ "name:'name',"
+				+ "value:'value'"
+			+ "}"
+		);
 	}
 
 	@Test
-	public void defaultsParser() throws Exception {
-		AnnotationList al = b.getAnnotationList();
-		CsvParser.create().applyAnnotations(al, null).build();
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
 	}
 
-	//-----------------------------------------------------------------------------------------------------------------
-	// No annotation.
-	//-----------------------------------------------------------------------------------------------------------------
-
-	static class C {}
-	static ClassInfo c = ClassInfo.of(C.class);
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
 
 	@Test
-	public void noAnnotationSerializer() throws Exception {
-		AnnotationList al = c.getAnnotationList();
-		CsvSerializer.create().applyAnnotations(al, null).build();
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
 	}
 
-	@Test
-	public void noAnnotationParser() throws Exception {
-		AnnotationList al = c.getAnnotationList();
-		CsvParser.create().applyAnnotations(al, null).build();
-	}
-}
\ No newline at end of file
+}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/CsvConfigAnnotationTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/HasQueryBuilder_Test.java
similarity index 60%
rename from juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/CsvConfigAnnotationTest.java
rename to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/HasQueryBuilder_Test.java
index d9091fa..da65518 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/csv/CsvConfigAnnotationTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/HasQueryBuilder_Test.java
@@ -10,56 +10,61 @@
 // * "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.csv;
+package org.apache.juneau.http.annotation;
 
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
 import static org.junit.runners.MethodSorters.*;
 
-import org.apache.juneau.csv.annotation.*;
-import org.apache.juneau.reflect.*;
+import org.apache.juneau.*;
 import org.junit.*;
 
-/**
- * Tests the @CsvConfig annotation.
- */
 @FixMethodOrder(NAME_ASCENDING)
-public class CsvConfigAnnotationTest {
+public class HasQueryBuilder_Test {
 
-	//-----------------------------------------------------------------------------------------------------------------
-	// Annotation with no values.
-	//-----------------------------------------------------------------------------------------------------------------
+	public static class X1 {}
 
-	@CsvConfig()
-	static class B {}
-	static ClassInfo b = ClassInfo.of(B.class);
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
 
-	@Test
-	public void defaultsSerializer() throws Exception {
-		AnnotationList al = b.getAnnotationList();
-		CsvSerializer.create().applyAnnotations(al, null).build();
-	}
+	HasQuery a1 = HasQueryBuilder.create()
+		.n("n")
+		.name("name")
+		.value("value")
+		.build();
+
+	HasQuery a2 = HasQueryBuilder.create()
+		.n("n")
+		.name("name")
+		.value("value")
+		.build();
 
 	@Test
-	public void defaultsParser() throws Exception {
-		AnnotationList al = b.getAnnotationList();
-		CsvParser.create().applyAnnotations(al, null).build();
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "n:'n',"
+				+ "name:'name',"
+				+ "value:'value'"
+			+ "}"
+		);
 	}
 
-	//-----------------------------------------------------------------------------------------------------------------
-	// No annotation.
-	//-----------------------------------------------------------------------------------------------------------------
-
-	static class C {}
-	static ClassInfo c = ClassInfo.of(C.class);
-
 	@Test
-	public void noAnnotationSerializer() throws Exception {
-		AnnotationList al = c.getAnnotationList();
-		CsvSerializer.create().applyAnnotations(al, null).build();
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
 	}
 
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
 	@Test
-	public void noAnnotationParser() throws Exception {
-		AnnotationList al = c.getAnnotationList();
-		CsvParser.create().applyAnnotations(al, null).build();
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
 	}
-}
\ No newline at end of file
+}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/HeaderBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/HeaderBuilder_Test.java
new file mode 100644
index 0000000..34f669f
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/HeaderBuilder_Test.java
@@ -0,0 +1,370 @@
+// ***************************************************************************************************************************
+// * 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.http.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.http.annotation.FormDataBuilder_Test.*;
+import org.apache.juneau.jsonschema.annotation.*;
+import org.apache.juneau.oapi.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class HeaderBuilder_Test {
+
+	private static final String CNAME = HeaderBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Header a1 = HeaderBuilder.create()
+		._default("default")
+		._enum("enum")
+		.aev(true)
+		.allowEmptyValue(true)
+		.api("api")
+		.cf("cf")
+		.collectionFormat("collectionFormat")
+		.d("d")
+		.description("description")
+		.df("df")
+		.e("e")
+		.emax(true)
+		.emin(true)
+		.ex("ex")
+		.example("example")
+		.exclusiveMaximum(true)
+		.exclusiveMinimum(true)
+		.f("f")
+		.format("format")
+		.items(ItemsBuilder.DEFAULT)
+		.max("max")
+		.maxi(1)
+		.maximum("maximum")
+		.maxItems(2)
+		.maxl(3)
+		.maxLength(4)
+		.min("min")
+		.mini(5)
+		.minimum("minimum")
+		.minItems(6)
+		.minl(7)
+		.minLength(8)
+		.mo("mo")
+		.multi(true)
+		.multipleOf("multipleOf")
+		.n("n")
+		.name("name")
+		.on("on")
+		.onClass(X1.class)
+		.p("p")
+		.parser(OpenApiParser.class)
+		.pattern("pattern")
+		.r(true)
+		.required(true)
+		.serializer(OpenApiSerializer.class)
+		.sie(true)
+		.skipIfEmpty(true)
+		.t("t")
+		.type("type")
+		.ui(true)
+		.uniqueItems(true)
+		.value("value")
+		.build();
+
+	Header a2 = HeaderBuilder.create()
+		._default("default")
+		._enum("enum")
+		.aev(true)
+		.allowEmptyValue(true)
+		.api("api")
+		.cf("cf")
+		.collectionFormat("collectionFormat")
+		.d("d")
+		.description("description")
+		.df("df")
+		.e("e")
+		.emax(true)
+		.emin(true)
+		.ex("ex")
+		.example("example")
+		.exclusiveMaximum(true)
+		.exclusiveMinimum(true)
+		.f("f")
+		.format("format")
+		.items(ItemsBuilder.DEFAULT)
+		.max("max")
+		.maxi(1)
+		.maximum("maximum")
+		.maxItems(2)
+		.maxl(3)
+		.maxLength(4)
+		.min("min")
+		.mini(5)
+		.minimum("minimum")
+		.minItems(6)
+		.minl(7)
+		.minLength(8)
+		.mo("mo")
+		.multi(true)
+		.multipleOf("multipleOf")
+		.n("n")
+		.name("name")
+		.on("on")
+		.onClass(X1.class)
+		.p("p")
+		.parser(OpenApiParser.class)
+		.pattern("pattern")
+		.r(true)
+		.required(true)
+		.serializer(OpenApiSerializer.class)
+		.sie(true)
+		.skipIfEmpty(true)
+		.t("t")
+		.type("type")
+		.ui(true)
+		.uniqueItems(true)
+		.value("value")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).stderr().json().is(""
+			+ "{"
+				+ "_default:['default'],"
+				+ "_enum:['enum'],"
+				+ "aev:true,"
+				+ "allowEmptyValue:true,"
+				+ "api:['api'],"
+				+ "cf:'cf',"
+				+ "collectionFormat:'collectionFormat',"
+				+ "d:['d'],"
+				+ "description:['description'],"
+				+ "df:['df'],"
+				+ "e:['e'],"
+				+ "emax:true,"
+				+ "emin:true,"
+				+ "ex:['ex'],"
+				+ "example:['example'],"
+				+ "exclusiveMaximum:true,"
+				+ "exclusiveMinimum:true,"
+				+ "f:'f',"
+				+ "format:'format',"
+				+ "items:{'$ref':'',_default:[],_enum:[],cf:'',collectionFormat:'',df:[],e:[],emax:false,emin:false,exclusiveMaximum:false,exclusiveMinimum:false,f:'',format:'',items:{'$ref':'',_default:[],_enum:[],cf:'',collectionFormat:'',df:[],e:[],emax:false,emin:false,exclusiveMaximum:false,exclusiveMinimum:false,f:'',format:'',items:[],max:'',maxItems:-1,maxLength:-1,maxi:-1,maximum:'',maxl:-1,min:'',minItems:-1,minLength:-1,mini:-1,minimum:'',minl:-1,mo:'',multipleOf:'',p:'',pattern:'',t:'',t [...]
+				+ "max:'max',"
+				+ "maxItems:2,"
+				+ "maxLength:4,"
+				+ "maxi:1,"
+				+ "maximum:'maximum',"
+				+ "maxl:3,"
+				+ "min:'min',"
+				+ "minItems:6,"
+				+ "minLength:8,"
+				+ "mini:5,"
+				+ "minimum:'minimum',"
+				+ "minl:7,"
+				+ "mo:'mo',"
+				+ "multi:true,"
+				+ "multipleOf:'multipleOf',"
+				+ "n:'n',"
+				+ "name:'name',"
+				+ "on:['on'],"
+				+ "onClass:['org.apache.juneau.http.annotation.FormDataBuilder_Test$X1'],"
+				+ "p:'p',"
+				+ "parser:'org.apache.juneau.oapi.OpenApiParser',"
+				+ "pattern:'pattern',"
+				+ "r:true,"
+				+ "required:true,"
+				+ "serializer:'org.apache.juneau.oapi.OpenApiSerializer',"
+				+ "sie:true,"
+				+ "skipIfEmpty:true,"
+				+ "t:'t',"
+				+ "type:'type',"
+				+ "ui:true,"
+				+ "uniqueItems:true,"
+				+ "value:'value'"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Header c1 = HeaderBuilder.create(C1.class).on(C2.class).build();
+		Header c2 = HeaderBuilder.create("a").on("b").build();
+		Header c3 = HeaderBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		Header c4 = HeaderBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Header(
+		_default="default",
+		_enum="enum",
+		aev=true,
+		allowEmptyValue=true,
+		api="api",
+		cf="cf",
+		collectionFormat="collectionFormat",
+		d="d",
+		description="description",
+		df="df",
+		e="e",
+		emax=true,
+		emin=true,
+		ex="ex",
+		example="example",
+		exclusiveMaximum=true,
+		exclusiveMinimum=true,
+		f="f",
+		format="format",
+		items=@Items,
+		max="max",
+		maxi=1,
+		maximum="maximum",
+		maxItems=2,
+		maxl=3,
+		maxLength=4,
+		min="min",
+		mini=5,
+		minimum="minimum",
+		minItems=6,
+		minl=7,
+		minLength=8,
+		mo="mo",
+		multi=true,
+		multipleOf="multipleOf",
+		n="n",
+		name="name",
+		on="on",
+		onClass=X1.class,
+		p="p",
+		parser=OpenApiParser.class,
+		pattern="pattern",
+		r=true,
+		required=true,
+		serializer=OpenApiSerializer.class,
+		sie=true,
+		skipIfEmpty=true,
+		t="t",
+		type="type",
+		ui=true,
+		uniqueItems=true,
+		value="value"
+	)
+	public static class D1 {}
+	Header d1 = D1.class.getAnnotationsByType(Header.class)[0];
+
+	@Header(
+		_default="default",
+		_enum="enum",
+		aev=true,
+		allowEmptyValue=true,
+		api="api",
+		cf="cf",
+		collectionFormat="collectionFormat",
+		d="d",
+		description="description",
+		df="df",
+		e="e",
+		emax=true,
+		emin=true,
+		ex="ex",
+		example="example",
+		exclusiveMaximum=true,
+		exclusiveMinimum=true,
+		f="f",
+		format="format",
+		items=@Items,
+		max="max",
+		maxi=1,
+		maximum="maximum",
+		maxItems=2,
+		maxl=3,
+		maxLength=4,
+		min="min",
+		mini=5,
+		minimum="minimum",
+		minItems=6,
+		minl=7,
+		minLength=8,
+		mo="mo",
+		multi=true,
+		multipleOf="multipleOf",
+		n="n",
+		name="name",
+		on="on",
+		onClass=X1.class,
+		p="p",
+		parser=OpenApiParser.class,
+		pattern="pattern",
+		r=true,
+		required=true,
+		serializer=OpenApiSerializer.class,
+		sie=true,
+		skipIfEmpty=true,
+		t="t",
+		type="type",
+		ui=true,
+		uniqueItems=true,
+		value="value"
+	)
+	public static class D2 {}
+	Header d2 = D2.class.getAnnotationsByType(Header.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/LicenseBuilder_Test.java
similarity index 53%
copy from juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreTest.java
copy to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/LicenseBuilder_Test.java
index fe34da0..c6a6a44 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/LicenseBuilder_Test.java
@@ -1,125 +1,98 @@
-// ***************************************************************************************************************************
-// * 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.annotation;
-
-import static org.apache.juneau.assertions.Assertions.*;
-import static org.junit.runners.MethodSorters.*;
-
-import org.apache.juneau.json.*;
-import org.junit.*;
-
-@FixMethodOrder(NAME_ASCENDING)
-public class BeanIgnoreTest {
-
-	//------------------------------------------------------------------------------------------------------------------
-	// Test @BeanIgnore on properties
-	//------------------------------------------------------------------------------------------------------------------
-
-	public static class A {
-		public String getA() {
-			return "a";
-		}
-
-		@BeanIgnore
-		public String getB() {
-			return "b";
-		}
-
-		public String c = "c";
-
-		@BeanIgnore public String d = "d";
-	}
-
-	@Test
-	public void testBeanIgnoreOnProperties() throws Exception {
-		assertObject(new A()).json().is("{c:'c',a:'a'}");
-	}
-
-	@BeanConfig(
-		applyBeanIgnore={
-			@BeanIgnore(on="Ac.getB"),
-			@BeanIgnore(on="Ac.d")
-		}
-	)
-	public static class Ac {
-		public String getA() {
-			return "a";
-		}
-
-		public String getB() {
-			return "b";
-		}
-
-		public String c = "c";
-
-		public String d = "d";
-	}
-
-	@Test
-	public void testBeanIgnoreOnProperties_usingConfig() throws Exception {
-		assertObject(new Ac()).serialized(SimpleJsonSerializer.DEFAULT.builder().applyAnnotations(Ac.class).build()).is("{c:'c',a:'a'}");
-	}
-
-	//------------------------------------------------------------------------------------------------------------------
-	// Test @BeanIgnore on classes
-	//------------------------------------------------------------------------------------------------------------------
-
-	@BeanIgnore
-	public static class B1 {
-		public int f = 1;
-
-		@Override
-		public String toString() {
-			return "xxx";
-		}
-	}
-
-	public static class B {
-		public int f2 = 2;
-		public B1 f3 = new B1();
-
-		public B1 getF4() {
-			return new B1();
-		}
-	}
-
-	@Test
-	public void testBeanIgnoreOnBean() throws Exception {
-		assertObject(new B()).json().is("{f2:2,f3:'xxx',f4:'xxx'}");
-	}
-
-	@BeanConfig(applyBeanIgnore=@BeanIgnore(on="B1c"))
-	public static class B1c {
-		public int f = 1;
-
-		@Override
-		public String toString() {
-			return "xxx";
-		}
-	}
-
-	public static class Bc {
-		public int f2 = 2;
-		public B1c f3 = new B1c();
-
-		public B1c getF4() {
-			return new B1c();
-		}
-	}
-
-	@Test
-	public void testBeanIgnoreOnBean_usingConfig() throws Exception {
-		assertObject(new Bc()).serialized(SimpleJsonSerializer.DEFAULT.builder().applyAnnotations(B1c.class).build()).is("{f2:2,f3:'xxx',f4:'xxx'}");
-	}
-}
-
+// ***************************************************************************************************************************
+// * 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.http.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class LicenseBuilder_Test {
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	License a1 = LicenseBuilder.create()
+		.name("name")
+		.url("url")
+		.value("value")
+		.build();
+
+	License a2 = LicenseBuilder.create()
+		.name("name")
+		.url("url")
+		.value("value")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "name:'name',"
+				+ "url:'url',"
+				+ "value:['value']"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@License(
+		name="name",
+		url="url",
+		value="value"
+	)
+	public static class D1 {}
+	License d1 = D1.class.getAnnotationsByType(License.class)[0];
+
+	@License(
+		name="name",
+		url="url",
+		value="value"
+	)
+	public static class D2 {}
+	License d2 = D2.class.getAnnotationsByType(License.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/PathBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/PathBuilder_Test.java
new file mode 100644
index 0000000..27b70af
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/PathBuilder_Test.java
@@ -0,0 +1,345 @@
+// ***************************************************************************************************************************
+// * 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.http.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.http.annotation.FormDataBuilder_Test.*;
+import org.apache.juneau.jsonschema.annotation.*;
+import org.apache.juneau.oapi.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class PathBuilder_Test {
+
+	private static final String CNAME = PathBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Path a1 = PathBuilder.create()
+		._enum("enum")
+		.aev(true)
+		.allowEmptyValue(true)
+		.api("api")
+		.cf("cf")
+		.collectionFormat("collectionFormat")
+		.d("d")
+		.description("description")
+		.e("e")
+		.emax(true)
+		.emin(true)
+		.ex("ex")
+		.example("example")
+		.exclusiveMaximum(true)
+		.exclusiveMinimum(true)
+		.f("f")
+		.format("format")
+		.items(ItemsBuilder.DEFAULT)
+		.max("max")
+		.maxi(1)
+		.maximum("maximum")
+		.maxItems(2)
+		.maxl(3)
+		.maxLength(4)
+		.min("min")
+		.mini(5)
+		.minimum("minimum")
+		.minItems(6)
+		.minl(7)
+		.minLength(8)
+		.mo("mo")
+		.multipleOf("multipleOf")
+		.n("n")
+		.name("name")
+		.on("on")
+		.onClass(X1.class)
+		.p("p")
+		.parser(OpenApiParser.class)
+		.pattern("pattern")
+		.r(true)
+		.required(true)
+		.serializer(OpenApiSerializer.class)
+		.t("t")
+		.type("type")
+		.ui(true)
+		.uniqueItems(true)
+		.value("value")
+		.build();
+
+	Path a2 = PathBuilder.create()
+		._enum("enum")
+		.aev(true)
+		.allowEmptyValue(true)
+		.api("api")
+		.cf("cf")
+		.collectionFormat("collectionFormat")
+		.d("d")
+		.description("description")
+		.e("e")
+		.emax(true)
+		.emin(true)
+		.ex("ex")
+		.example("example")
+		.exclusiveMaximum(true)
+		.exclusiveMinimum(true)
+		.f("f")
+		.format("format")
+		.items(ItemsBuilder.DEFAULT)
+		.max("max")
+		.maxi(1)
+		.maximum("maximum")
+		.maxItems(2)
+		.maxl(3)
+		.maxLength(4)
+		.min("min")
+		.mini(5)
+		.minimum("minimum")
+		.minItems(6)
+		.minl(7)
+		.minLength(8)
+		.mo("mo")
+		.multipleOf("multipleOf")
+		.n("n")
+		.name("name")
+		.on("on")
+		.onClass(X1.class)
+		.p("p")
+		.parser(OpenApiParser.class)
+		.pattern("pattern")
+		.r(true)
+		.required(true)
+		.serializer(OpenApiSerializer.class)
+		.t("t")
+		.type("type")
+		.ui(true)
+		.uniqueItems(true)
+		.value("value")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "_enum:['enum'],"
+				+ "aev:true,"
+				+ "allowEmptyValue:true,"
+				+ "api:['api'],"
+				+ "cf:'cf',"
+				+ "collectionFormat:'collectionFormat',"
+				+ "d:['d'],"
+				+ "description:['description'],"
+				+ "e:['e'],"
+				+ "emax:true,"
+				+ "emin:true,"
+				+ "ex:['ex'],"
+				+ "example:['example'],"
+				+ "exclusiveMaximum:true,"
+				+ "exclusiveMinimum:true,"
+				+ "f:'f',"
+				+ "format:'format',"
+				+ "items:{'$ref':'',_default:[],_enum:[],cf:'',collectionFormat:'',df:[],e:[],emax:false,emin:false,exclusiveMaximum:false,exclusiveMinimum:false,f:'',format:'',items:{'$ref':'',_default:[],_enum:[],cf:'',collectionFormat:'',df:[],e:[],emax:false,emin:false,exclusiveMaximum:false,exclusiveMinimum:false,f:'',format:'',items:[],max:'',maxItems:-1,maxLength:-1,maxi:-1,maximum:'',maxl:-1,min:'',minItems:-1,minLength:-1,mini:-1,minimum:'',minl:-1,mo:'',multipleOf:'',p:'',pattern:'',t:'',t [...]
+				+ "max:'max',"
+				+ "maxItems:2,"
+				+ "maxLength:4,"
+				+ "maxi:1,"
+				+ "maximum:'maximum',"
+				+ "maxl:3,"
+				+ "min:'min',"
+				+ "minItems:6,"
+				+ "minLength:8,"
+				+ "mini:5,"
+				+ "minimum:'minimum',"
+				+ "minl:7,"
+				+ "mo:'mo',"
+				+ "multipleOf:'multipleOf',"
+				+ "n:'n',"
+				+ "name:'name',"
+				+ "on:['on'],"
+				+ "onClass:['org.apache.juneau.http.annotation.FormDataBuilder_Test$X1'],"
+				+ "p:'p',"
+				+ "parser:'org.apache.juneau.oapi.OpenApiParser',"
+				+ "pattern:'pattern',"
+				+ "r:true,"
+				+ "required:true,"
+				+ "serializer:'org.apache.juneau.oapi.OpenApiSerializer',"
+				+ "t:'t',"
+				+ "type:'type',"
+				+ "ui:true,"
+				+ "uniqueItems:true,"
+				+ "value:'value'"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Path c1 = PathBuilder.create(C1.class).on(C2.class).build();
+		Path c2 = PathBuilder.create("a").on("b").build();
+		Path c3 = PathBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		Path c4 = PathBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Path(
+		_enum="enum",
+		aev=true,
+		allowEmptyValue=true,
+		api="api",
+		cf="cf",
+		collectionFormat="collectionFormat",
+		d="d",
+		description="description",
+		e="e",
+		emax=true,
+		emin=true,
+		ex="ex",
+		example="example",
+		exclusiveMaximum=true,
+		exclusiveMinimum=true,
+		f="f",
+		format="format",
+		items=@Items,
+		max="max",
+		maxi=1,
+		maximum="maximum",
+		maxItems=2,
+		maxl=3,
+		maxLength=4,
+		min="min",
+		mini=5,
+		minimum="minimum",
+		minItems=6,
+		minl=7,
+		minLength=8,
+		mo="mo",
+		multipleOf="multipleOf",
+		n="n",
+		name="name",
+		on="on",
+		onClass=X1.class,
+		p="p",
+		parser=OpenApiParser.class,
+		pattern="pattern",
+		r=true,
+		required=true,
+		serializer=OpenApiSerializer.class,
+		t="t",
+		type="type",
+		ui=true,
+		uniqueItems=true,
+		value="value"
+	)
+	public static class D1 {}
+	Path d1 = D1.class.getAnnotationsByType(Path.class)[0];
+
+	@Path(
+		_enum="enum",
+		aev=true,
+		allowEmptyValue=true,
+		api="api",
+		cf="cf",
+		collectionFormat="collectionFormat",
+		d="d",
+		description="description",
+		e="e",
+		emax=true,
+		emin=true,
+		ex="ex",
+		example="example",
+		exclusiveMaximum=true,
+		exclusiveMinimum=true,
+		f="f",
+		format="format",
+		items=@Items,
+		max="max",
+		maxi=1,
+		maximum="maximum",
+		maxItems=2,
+		maxl=3,
+		maxLength=4,
+		min="min",
+		mini=5,
+		minimum="minimum",
+		minItems=6,
+		minl=7,
+		minLength=8,
+		mo="mo",
+		multipleOf="multipleOf",
+		n="n",
+		name="name",
+		on="on",
+		onClass=X1.class,
+		p="p",
+		parser=OpenApiParser.class,
+		pattern="pattern",
+		r=true,
+		required=true,
+		serializer=OpenApiSerializer.class,
+		t="t",
+		type="type",
+		ui=true,
+		uniqueItems=true,
+		value="value"
+	)
+	public static class D2 {}
+	Path d2 = D2.class.getAnnotationsByType(Path.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/QueryBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/QueryBuilder_Test.java
new file mode 100644
index 0000000..6d97575
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/QueryBuilder_Test.java
@@ -0,0 +1,370 @@
+// ***************************************************************************************************************************
+// * 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.http.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.http.annotation.FormDataBuilder_Test.*;
+import org.apache.juneau.jsonschema.annotation.*;
+import org.apache.juneau.oapi.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class QueryBuilder_Test {
+
+	private static final String CNAME = QueryBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Query a1 = QueryBuilder.create()
+		._default("default")
+		._enum("enum")
+		.aev(true)
+		.allowEmptyValue(true)
+		.api("api")
+		.cf("cf")
+		.collectionFormat("collectionFormat")
+		.d("d")
+		.description("description")
+		.df("df")
+		.e("e")
+		.emax(true)
+		.emin(true)
+		.ex("ex")
+		.example("example")
+		.exclusiveMaximum(true)
+		.exclusiveMinimum(true)
+		.f("f")
+		.format("format")
+		.items(ItemsBuilder.DEFAULT)
+		.max("max")
+		.maxi(1)
+		.maximum("maximum")
+		.maxItems(2)
+		.maxl(3)
+		.maxLength(4)
+		.min("min")
+		.mini(5)
+		.minimum("minimum")
+		.minItems(6)
+		.minl(7)
+		.minLength(8)
+		.mo("mo")
+		.multi(true)
+		.multipleOf("multipleOf")
+		.n("n")
+		.name("name")
+		.on("on")
+		.onClass(X1.class)
+		.p("p")
+		.parser(OpenApiParser.class)
+		.pattern("pattern")
+		.r(true)
+		.required(true)
+		.serializer(OpenApiSerializer.class)
+		.sie(true)
+		.skipIfEmpty(true)
+		.t("t")
+		.type("type")
+		.ui(true)
+		.uniqueItems(true)
+		.value("value")
+		.build();
+
+	Query a2 = QueryBuilder.create()
+		._default("default")
+		._enum("enum")
+		.aev(true)
+		.allowEmptyValue(true)
+		.api("api")
+		.cf("cf")
+		.collectionFormat("collectionFormat")
+		.d("d")
+		.description("description")
+		.df("df")
+		.e("e")
+		.emax(true)
+		.emin(true)
+		.ex("ex")
+		.example("example")
+		.exclusiveMaximum(true)
+		.exclusiveMinimum(true)
+		.f("f")
+		.format("format")
+		.items(ItemsBuilder.DEFAULT)
+		.max("max")
+		.maxi(1)
+		.maximum("maximum")
+		.maxItems(2)
+		.maxl(3)
+		.maxLength(4)
+		.min("min")
+		.mini(5)
+		.minimum("minimum")
+		.minItems(6)
+		.minl(7)
+		.minLength(8)
+		.mo("mo")
+		.multi(true)
+		.multipleOf("multipleOf")
+		.n("n")
+		.name("name")
+		.on("on")
+		.onClass(X1.class)
+		.p("p")
+		.parser(OpenApiParser.class)
+		.pattern("pattern")
+		.r(true)
+		.required(true)
+		.serializer(OpenApiSerializer.class)
+		.sie(true)
+		.skipIfEmpty(true)
+		.t("t")
+		.type("type")
+		.ui(true)
+		.uniqueItems(true)
+		.value("value")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "_default:['default'],"
+				+ "_enum:['enum'],"
+				+ "aev:true,"
+				+ "allowEmptyValue:true,"
+				+ "api:['api'],"
+				+ "cf:'cf',"
+				+ "collectionFormat:'collectionFormat',"
+				+ "d:['d'],"
+				+ "description:['description'],"
+				+ "df:['df'],"
+				+ "e:['e'],"
+				+ "emax:true,"
+				+ "emin:true,"
+				+ "ex:['ex'],"
+				+ "example:['example'],"
+				+ "exclusiveMaximum:true,"
+				+ "exclusiveMinimum:true,"
+				+ "f:'f',"
+				+ "format:'format',"
+				+ "items:{'$ref':'',_default:[],_enum:[],cf:'',collectionFormat:'',df:[],e:[],emax:false,emin:false,exclusiveMaximum:false,exclusiveMinimum:false,f:'',format:'',items:{'$ref':'',_default:[],_enum:[],cf:'',collectionFormat:'',df:[],e:[],emax:false,emin:false,exclusiveMaximum:false,exclusiveMinimum:false,f:'',format:'',items:[],max:'',maxItems:-1,maxLength:-1,maxi:-1,maximum:'',maxl:-1,min:'',minItems:-1,minLength:-1,mini:-1,minimum:'',minl:-1,mo:'',multipleOf:'',p:'',pattern:'',t:'',t [...]
+				+ "max:'max',"
+				+ "maxItems:2,"
+				+ "maxLength:4,"
+				+ "maxi:1,"
+				+ "maximum:'maximum',"
+				+ "maxl:3,"
+				+ "min:'min',"
+				+ "minItems:6,"
+				+ "minLength:8,"
+				+ "mini:5,"
+				+ "minimum:'minimum',"
+				+ "minl:7,"
+				+ "mo:'mo',"
+				+ "multi:true,"
+				+ "multipleOf:'multipleOf',"
+				+ "n:'n',"
+				+ "name:'name',"
+				+ "on:['on'],"
+				+ "onClass:['org.apache.juneau.http.annotation.FormDataBuilder_Test$X1'],"
+				+ "p:'p',"
+				+ "parser:'org.apache.juneau.oapi.OpenApiParser',"
+				+ "pattern:'pattern',"
+				+ "r:true,"
+				+ "required:true,"
+				+ "serializer:'org.apache.juneau.oapi.OpenApiSerializer',"
+				+ "sie:true,"
+				+ "skipIfEmpty:true,"
+				+ "t:'t',"
+				+ "type:'type',"
+				+ "ui:true,"
+				+ "uniqueItems:true,"
+				+ "value:'value'"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Query c1 = QueryBuilder.create(C1.class).on(C2.class).build();
+		Query c2 = QueryBuilder.create("a").on("b").build();
+		Query c3 = QueryBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		Query c4 = QueryBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Query(
+		_default="default",
+		_enum="enum",
+		aev=true,
+		allowEmptyValue=true,
+		api="api",
+		cf="cf",
+		collectionFormat="collectionFormat",
+		d="d",
+		description="description",
+		df="df",
+		e="e",
+		emax=true,
+		emin=true,
+		ex="ex",
+		example="example",
+		exclusiveMaximum=true,
+		exclusiveMinimum=true,
+		f="f",
+		format="format",
+		items=@Items,
+		max="max",
+		maxi=1,
+		maximum="maximum",
+		maxItems=2,
+		maxl=3,
+		maxLength=4,
+		min="min",
+		mini=5,
+		minimum="minimum",
+		minItems=6,
+		minl=7,
+		minLength=8,
+		mo="mo",
+		multi=true,
+		multipleOf="multipleOf",
+		n="n",
+		name="name",
+		on="on",
+		onClass=X1.class,
+		p="p",
+		parser=OpenApiParser.class,
+		pattern="pattern",
+		r=true,
+		required=true,
+		serializer=OpenApiSerializer.class,
+		sie=true,
+		skipIfEmpty=true,
+		t="t",
+		type="type",
+		ui=true,
+		uniqueItems=true,
+		value="value"
+	)
+	public static class D1 {}
+	Query d1 = D1.class.getAnnotationsByType(Query.class)[0];
+
+	@Query(
+		_default="default",
+		_enum="enum",
+		aev=true,
+		allowEmptyValue=true,
+		api="api",
+		cf="cf",
+		collectionFormat="collectionFormat",
+		d="d",
+		description="description",
+		df="df",
+		e="e",
+		emax=true,
+		emin=true,
+		ex="ex",
+		example="example",
+		exclusiveMaximum=true,
+		exclusiveMinimum=true,
+		f="f",
+		format="format",
+		items=@Items,
+		max="max",
+		maxi=1,
+		maximum="maximum",
+		maxItems=2,
+		maxl=3,
+		maxLength=4,
+		min="min",
+		mini=5,
+		minimum="minimum",
+		minItems=6,
+		minl=7,
+		minLength=8,
+		mo="mo",
+		multi=true,
+		multipleOf="multipleOf",
+		n="n",
+		name="name",
+		on="on",
+		onClass=X1.class,
+		p="p",
+		parser=OpenApiParser.class,
+		pattern="pattern",
+		r=true,
+		required=true,
+		serializer=OpenApiSerializer.class,
+		sie=true,
+		skipIfEmpty=true,
+		t="t",
+		type="type",
+		ui=true,
+		uniqueItems=true,
+		value="value"
+	)
+	public static class D2 {}
+	Query d2 = D2.class.getAnnotationsByType(Query.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/RequestBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/RequestBuilder_Test.java
new file mode 100644
index 0000000..10ab3df
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/RequestBuilder_Test.java
@@ -0,0 +1,126 @@
+// ***************************************************************************************************************************
+// * 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.http.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.oapi.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class RequestBuilder_Test {
+
+	private static final String CNAME = RequestBuilder_Test.class.getName();
+
+	public static class X1 {}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Request a1 = RequestBuilder.create()
+		.on("on")
+		.onClass(X1.class)
+		.parser(OpenApiParser.class)
+		.serializer(OpenApiSerializer.class)
+		.build();
+
+	Request a2 = RequestBuilder.create()
+		.on("on")
+		.onClass(X1.class)
+		.parser(OpenApiParser.class)
+		.serializer(OpenApiSerializer.class)
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "on:['on'],"
+				+ "onClass:['"+CNAME+"$X1'],"
+				+ "parser:'org.apache.juneau.oapi.OpenApiParser',"
+				+ "serializer:'org.apache.juneau.oapi.OpenApiSerializer'"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Request c1 = RequestBuilder.create(C1.class).on(C2.class).build();
+		Request c2 = RequestBuilder.create("a").on("b").build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Request(
+		on="on",
+		onClass=X1.class,
+		parser=OpenApiParser.class,
+		serializer=OpenApiSerializer.class
+	)
+	public static class D1 {}
+	Request d1 = D1.class.getAnnotationsByType(Request.class)[0];
+
+	@Request(
+		on="on",
+		onClass=X1.class,
+		parser=OpenApiParser.class,
+		serializer=OpenApiSerializer.class
+	)
+	public static class D2 {}
+	Request d2 = D2.class.getAnnotationsByType(Request.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/ResponseBodyBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/ResponseBodyBuilder_Test.java
new file mode 100644
index 0000000..b2ff2e7
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/ResponseBodyBuilder_Test.java
@@ -0,0 +1,117 @@
+// ***************************************************************************************************************************
+// * 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.http.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class ResponseBodyBuilder_Test {
+
+	private static final String CNAME = ResponseBodyBuilder_Test.class.getName();
+
+	public static class X1 {}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	ResponseBody a1 = ResponseBodyBuilder.create()
+		.on("on")
+		.onClass(X1.class)
+		.build();
+
+	ResponseBody a2 = ResponseBodyBuilder.create()
+		.on("on")
+		.onClass(X1.class)
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "on:['on'],"
+				+ "onClass:['"+CNAME+"$X1']"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		ResponseBody c1 = ResponseBodyBuilder.create(C1.class).on(C2.class).build();
+		ResponseBody c2 = ResponseBodyBuilder.create("a").on("b").build();
+		ResponseBody c4 = ResponseBodyBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@ResponseBody(
+		on="on",
+		onClass=X1.class
+	)
+	public static class D1 {}
+	ResponseBody d1 = D1.class.getAnnotationsByType(ResponseBody.class)[0];
+
+	@ResponseBody(
+		on="on",
+		onClass=X1.class
+	)
+	public static class D2 {}
+	ResponseBody d2 = D2.class.getAnnotationsByType(ResponseBody.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/ResponseBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/ResponseBuilder_Test.java
new file mode 100644
index 0000000..91da1b0
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/ResponseBuilder_Test.java
@@ -0,0 +1,184 @@
+// ***************************************************************************************************************************
+// * 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.http.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.jsonschema.annotation.*;
+import org.apache.juneau.oapi.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class ResponseBuilder_Test {
+
+	private static final String CNAME = ResponseBuilder_Test.class.getName();
+
+	public static class X1 {}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Response a1 = ResponseBuilder.create()
+		.api("api")
+		.code(1)
+		.d("d")
+		.description("description")
+		.ex("ex")
+		.example("example")
+		.examples("examples")
+		.exs("exs")
+		.headers(ResponseHeaderBuilder.DEFAULT)
+		.on("on")
+		.onClass(X1.class)
+		.parser(OpenApiParser.class)
+		.schema(SchemaBuilder.DEFAULT)
+		.serializer(OpenApiSerializer.class)
+		.value(2)
+		.build();
+
+	Response a2 = ResponseBuilder.create()
+		.api("api")
+		.code(1)
+		.d("d")
+		.description("description")
+		.ex("ex")
+		.example("example")
+		.examples("examples")
+		.exs("exs")
+		.headers(ResponseHeaderBuilder.DEFAULT)
+		.on("on")
+		.onClass(X1.class)
+		.parser(OpenApiParser.class)
+		.schema(SchemaBuilder.DEFAULT)
+		.serializer(OpenApiSerializer.class)
+		.value(2)
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).stderr().json().is(""
+			+ "{"
+				+ "api:['api'],"
+				+ "code:[1],"
+				+ "d:['d'],"
+				+ "description:['description'],"
+				+ "ex:['ex'],"
+				+ "example:['example'],"
+				+ "examples:['examples'],"
+				+ "exs:['exs'],"
+				+ "headers:[{'$ref':'',_default:[],_enum:[],api:[],cf:'',code:[],collectionFormat:'',d:[],description:[],df:[],e:[],emax:false,emin:false,ex:[],example:[],exclusiveMaximum:false,exclusiveMinimum:false,f:'',format:'',items:{'$ref':'',_default:[],_enum:[],cf:'',collectionFormat:'',df:[],e:[],emax:false,emin:false,exclusiveMaximum:false,exclusiveMinimum:false,f:'',format:'',items:{'$ref':'',_default:[],_enum:[],cf:'',collectionFormat:'',df:[],e:[],emax:false,emin:false,exclusiveMaximum: [...]
+				+ "on:['on']"
+				+ ",onClass:['"+CNAME+"$X1'],"
+				+ "parser:'org.apache.juneau.oapi.OpenApiParser',"
+				+ "schema:{'$ref':'',_default:[],_enum:[],additionalProperties:[],allOf:[],cf:'',collectionFormat:'',d:[],description:[],df:[],discriminator:'',e:[],emax:false,emin:false,ex:[],example:[],examples:[],exclusiveMaximum:false,exclusiveMinimum:false,exs:[],externalDocs:{description:[],url:'',value:[]},f:'',format:'',ignore:false,items:{'$ref':'',_default:[],_enum:[],cf:'',collectionFormat:'',df:[],e:[],emax:false,emin:false,exclusiveMaximum:false,exclusiveMinimum:false,f:'',format:'',ite [...]
+				+ "serializer:'org.apache.juneau.oapi.OpenApiSerializer',"
+				+ "value:[2]"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Response c1 = ResponseBuilder.create(C1.class).on(C2.class).build();
+		Response c2 = ResponseBuilder.create("a").on("b").build();
+		Response c4 = ResponseBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Response(
+		api="api",
+		code=1,
+		d="d",
+		description="description",
+		ex="ex",
+		example="example",
+		examples="examples",
+		exs="exs",
+		headers=@ResponseHeader,
+		on="on",
+		onClass=X1.class,
+		parser=OpenApiParser.class,
+		schema=@Schema,
+		serializer=OpenApiSerializer.class,
+		value=2
+	)
+	public static class D1 {}
+	Response d1 = D1.class.getAnnotationsByType(Response.class)[0];
+
+	@Response(
+		api="api",
+		code=1,
+		d="d",
+		description="description",
+		ex="ex",
+		example="example",
+		examples="examples",
+		exs="exs",
+		headers=@ResponseHeader,
+		on="on",
+		onClass=X1.class,
+		parser=OpenApiParser.class,
+		schema=@Schema,
+		serializer=OpenApiSerializer.class,
+		value=2
+	)
+	public static class D2 {}
+	Response d2 = D2.class.getAnnotationsByType(Response.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/ResponseHeaderBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/ResponseHeaderBuilder_Test.java
new file mode 100644
index 0000000..a718cda
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/ResponseHeaderBuilder_Test.java
@@ -0,0 +1,338 @@
+// ***************************************************************************************************************************
+// * 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.http.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.http.annotation.FormDataBuilder_Test.*;
+import org.apache.juneau.jsonschema.annotation.*;
+import org.apache.juneau.oapi.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class ResponseHeaderBuilder_Test {
+
+	private static final String CNAME = ResponseHeaderBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	ResponseHeader a1 = ResponseHeaderBuilder.create()
+		.$ref("$ref")
+		._default("default")
+		._enum("enum")
+		.api("api")
+		.cf("cf")
+		.code(1)
+		.collectionFormat("collectionFormat")
+		.d("d")
+		.description("description")
+		.df("df")
+		.e("e")
+		.emax(true)
+		.emin(true)
+		.ex("ex")
+		.example("example")
+		.exclusiveMaximum(true)
+		.exclusiveMinimum(true)
+		.f("f")
+		.format("format")
+		.items(ItemsBuilder.DEFAULT)
+		.max("max")
+		.maxi(1)
+		.maximum("maximum")
+		.maxItems(2)
+		.maxl(3)
+		.maxLength(4)
+		.min("min")
+		.mini(5)
+		.minimum("minimum")
+		.minItems(6)
+		.minl(7)
+		.minLength(8)
+		.mo("mo")
+		.multipleOf("multipleOf")
+		.n("n")
+		.name("name")
+		.on("on")
+		.onClass(X1.class)
+		.p("p")
+		.pattern("pattern")
+		.serializer(OpenApiSerializer.class)
+		.t("t")
+		.type("type")
+		.ui(true)
+		.uniqueItems(true)
+		.value("value")
+		.build();
+
+	ResponseHeader a2 = ResponseHeaderBuilder.create()
+		.$ref("$ref")
+		._default("default")
+		._enum("enum")
+		.api("api")
+		.cf("cf")
+		.code(1)
+		.collectionFormat("collectionFormat")
+		.d("d")
+		.description("description")
+		.df("df")
+		.e("e")
+		.emax(true)
+		.emin(true)
+		.ex("ex")
+		.example("example")
+		.exclusiveMaximum(true)
+		.exclusiveMinimum(true)
+		.f("f")
+		.format("format")
+		.items(ItemsBuilder.DEFAULT)
+		.max("max")
+		.maxi(1)
+		.maximum("maximum")
+		.maxItems(2)
+		.maxl(3)
+		.maxLength(4)
+		.min("min")
+		.mini(5)
+		.minimum("minimum")
+		.minItems(6)
+		.minl(7)
+		.minLength(8)
+		.mo("mo")
+		.multipleOf("multipleOf")
+		.n("n")
+		.name("name")
+		.on("on")
+		.onClass(X1.class)
+		.p("p")
+		.pattern("pattern")
+		.serializer(OpenApiSerializer.class)
+		.t("t")
+		.type("type")
+		.ui(true)
+		.uniqueItems(true)
+		.value("value")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).stderr().json().is(""
+			+ "{"
+				+ "'$ref':'$ref',"
+				+ "_default:['default'],"
+				+ "_enum:['enum'],"
+				+ "api:['api'],"
+				+ "cf:'cf',"
+				+ "code:[1],"
+				+ "collectionFormat:'collectionFormat',"
+				+ "d:['d'],"
+				+ "description:['description'],"
+				+ "df:['df'],"
+				+ "e:['e'],"
+				+ "emax:true,"
+				+ "emin:true,"
+				+ "ex:['ex'],"
+				+ "example:['example'],"
+				+ "exclusiveMaximum:true,"
+				+ "exclusiveMinimum:true,"
+				+ "f:'f',"
+				+ "format:'format',"
+				+ "items:{'$ref':'',_default:[],_enum:[],cf:'',collectionFormat:'',df:[],e:[],emax:false,emin:false,exclusiveMaximum:false,exclusiveMinimum:false,f:'',format:'',items:{'$ref':'',_default:[],_enum:[],cf:'',collectionFormat:'',df:[],e:[],emax:false,emin:false,exclusiveMaximum:false,exclusiveMinimum:false,f:'',format:'',items:[],max:'',maxItems:-1,maxLength:-1,maxi:-1,maximum:'',maxl:-1,min:'',minItems:-1,minLength:-1,mini:-1,minimum:'',minl:-1,mo:'',multipleOf:'',p:'',pattern:'',t:'',t [...]
+				+ "max:'max',"
+				+ "maxItems:2,"
+				+ "maxLength:4,"
+				+ "maxi:1,"
+				+ "maximum:'maximum',"
+				+ "maxl:3,"
+				+ "min:'min',"
+				+ "minItems:6,"
+				+ "minLength:8,"
+				+ "mini:5,"
+				+ "minimum:'minimum',"
+				+ "minl:7,"
+				+ "mo:'mo',"
+				+ "multipleOf:'multipleOf',"
+				+ "n:'n',"
+				+ "name:'name',"
+				+ "on:['on'],"
+				+ "onClass:['org.apache.juneau.http.annotation.FormDataBuilder_Test$X1'],"
+				+ "p:'p',"
+				+ "pattern:'pattern',"
+				+ "serializer:'org.apache.juneau.oapi.OpenApiSerializer',"
+				+ "t:'t',"
+				+ "type:'type',"
+				+ "ui:true,"
+				+ "uniqueItems:true,"
+				+ "value:'value'"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		ResponseHeader c1 = ResponseHeaderBuilder.create(C1.class).on(C2.class).build();
+		ResponseHeader c2 = ResponseHeaderBuilder.create("a").on("b").build();
+		ResponseHeader c4 = ResponseHeaderBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@ResponseHeader(
+		$ref="$ref",
+		_default="default",
+		_enum="enum",
+		api="api",
+		cf="cf",
+		code=1,
+		collectionFormat="collectionFormat",
+		d="d",
+		description="description",
+		df="df",
+		e="e",
+		emax=true,
+		emin=true,
+		ex="ex",
+		example="example",
+		exclusiveMaximum=true,
+		exclusiveMinimum=true,
+		f="f",
+		format="format",
+		items=@Items,
+		max="max",
+		maxi=1,
+		maximum="maximum",
+		maxItems=2,
+		maxl=3,
+		maxLength=4,
+		min="min",
+		mini=5,
+		minimum="minimum",
+		minItems=6,
+		minl=7,
+		minLength=8,
+		mo="mo",
+		multipleOf="multipleOf",
+		n="n",
+		name="name",
+		on="on",
+		onClass=X1.class,
+		p="p",
+		pattern="pattern",
+		serializer=OpenApiSerializer.class,
+		t="t",
+		type="type",
+		ui=true,
+		uniqueItems=true,
+		value="value"
+	)
+	public static class D1 {}
+	ResponseHeader d1 = D1.class.getAnnotationsByType(ResponseHeader.class)[0];
+
+	@ResponseHeader(
+		$ref="$ref",
+		_default="default",
+		_enum="enum",
+		api="api",
+		cf="cf",
+		code=1,
+		collectionFormat="collectionFormat",
+		d="d",
+		description="description",
+		df="df",
+		e="e",
+		emax=true,
+		emin=true,
+		ex="ex",
+		example="example",
+		exclusiveMaximum=true,
+		exclusiveMinimum=true,
+		f="f",
+		format="format",
+		items=@Items,
+		max="max",
+		maxi=1,
+		maximum="maximum",
+		maxItems=2,
+		maxl=3,
+		maxLength=4,
+		min="min",
+		mini=5,
+		minimum="minimum",
+		minItems=6,
+		minl=7,
+		minLength=8,
+		mo="mo",
+		multipleOf="multipleOf",
+		n="n",
+		name="name",
+		on="on",
+		onClass=X1.class,
+		p="p",
+		pattern="pattern",
+		serializer=OpenApiSerializer.class,
+		t="t",
+		type="type",
+		ui=true,
+		uniqueItems=true,
+		value="value"
+	)
+	public static class D2 {}
+	ResponseHeader d2 = D2.class.getAnnotationsByType(ResponseHeader.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/ResponseStatusBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/ResponseStatusBuilder_Test.java
new file mode 100644
index 0000000..faeefda
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/ResponseStatusBuilder_Test.java
@@ -0,0 +1,117 @@
+// ***************************************************************************************************************************
+// * 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.http.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class ResponseStatusBuilder_Test {
+
+	private static final String CNAME = ResponseStatusBuilder_Test.class.getName();
+
+	public static class X1 {}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	ResponseStatus a1 = ResponseStatusBuilder.create()
+		.on("on")
+		.onClass(X1.class)
+		.build();
+
+	ResponseStatus a2 = ResponseStatusBuilder.create()
+		.on("on")
+		.onClass(X1.class)
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "on:['on'],"
+				+ "onClass:['"+CNAME+"$X1']"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		ResponseStatus c1 = ResponseStatusBuilder.create(C1.class).on(C2.class).build();
+		ResponseStatus c2 = ResponseStatusBuilder.create("a").on("b").build();
+		ResponseStatus c4 = ResponseStatusBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@ResponseStatus(
+		on="on",
+		onClass=X1.class
+	)
+	public static class D1 {}
+	ResponseStatus d1 = D1.class.getAnnotationsByType(ResponseStatus.class)[0];
+
+	@ResponseStatus(
+		on="on",
+		onClass=X1.class
+	)
+	public static class D2 {}
+	ResponseStatus d2 = D2.class.getAnnotationsByType(ResponseStatus.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/TagBuilder_Test.java
similarity index 50%
copy from juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreTest.java
copy to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/TagBuilder_Test.java
index fe34da0..3084219 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/annotation/TagBuilder_Test.java
@@ -1,125 +1,104 @@
-// ***************************************************************************************************************************
-// * 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.annotation;
-
-import static org.apache.juneau.assertions.Assertions.*;
-import static org.junit.runners.MethodSorters.*;
-
-import org.apache.juneau.json.*;
-import org.junit.*;
-
-@FixMethodOrder(NAME_ASCENDING)
-public class BeanIgnoreTest {
-
-	//------------------------------------------------------------------------------------------------------------------
-	// Test @BeanIgnore on properties
-	//------------------------------------------------------------------------------------------------------------------
-
-	public static class A {
-		public String getA() {
-			return "a";
-		}
-
-		@BeanIgnore
-		public String getB() {
-			return "b";
-		}
-
-		public String c = "c";
-
-		@BeanIgnore public String d = "d";
-	}
-
-	@Test
-	public void testBeanIgnoreOnProperties() throws Exception {
-		assertObject(new A()).json().is("{c:'c',a:'a'}");
-	}
-
-	@BeanConfig(
-		applyBeanIgnore={
-			@BeanIgnore(on="Ac.getB"),
-			@BeanIgnore(on="Ac.d")
-		}
-	)
-	public static class Ac {
-		public String getA() {
-			return "a";
-		}
-
-		public String getB() {
-			return "b";
-		}
-
-		public String c = "c";
-
-		public String d = "d";
-	}
-
-	@Test
-	public void testBeanIgnoreOnProperties_usingConfig() throws Exception {
-		assertObject(new Ac()).serialized(SimpleJsonSerializer.DEFAULT.builder().applyAnnotations(Ac.class).build()).is("{c:'c',a:'a'}");
-	}
-
-	//------------------------------------------------------------------------------------------------------------------
-	// Test @BeanIgnore on classes
-	//------------------------------------------------------------------------------------------------------------------
-
-	@BeanIgnore
-	public static class B1 {
-		public int f = 1;
-
-		@Override
-		public String toString() {
-			return "xxx";
-		}
-	}
-
-	public static class B {
-		public int f2 = 2;
-		public B1 f3 = new B1();
-
-		public B1 getF4() {
-			return new B1();
-		}
-	}
-
-	@Test
-	public void testBeanIgnoreOnBean() throws Exception {
-		assertObject(new B()).json().is("{f2:2,f3:'xxx',f4:'xxx'}");
-	}
-
-	@BeanConfig(applyBeanIgnore=@BeanIgnore(on="B1c"))
-	public static class B1c {
-		public int f = 1;
-
-		@Override
-		public String toString() {
-			return "xxx";
-		}
-	}
-
-	public static class Bc {
-		public int f2 = 2;
-		public B1c f3 = new B1c();
-
-		public B1c getF4() {
-			return new B1c();
-		}
-	}
-
-	@Test
-	public void testBeanIgnoreOnBean_usingConfig() throws Exception {
-		assertObject(new Bc()).serialized(SimpleJsonSerializer.DEFAULT.builder().applyAnnotations(B1c.class).build()).is("{f2:2,f3:'xxx',f4:'xxx'}");
-	}
-}
-
+// ***************************************************************************************************************************
+// * 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.http.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.jsonschema.annotation.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class TagBuilder_Test {
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Tag a1 = TagBuilder.create()
+		.description("description")
+		.externalDocs(ExternalDocsBuilder.DEFAULT)
+		.name("name")
+		.value("value")
+		.build();
+
+	Tag a2 = TagBuilder.create()
+		.description("description")
+		.externalDocs(ExternalDocsBuilder.DEFAULT)
+		.name("name")
+		.value("value")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).stderr().json().is(""
+			+ "{"
+				+ "description:['description'],"
+				+ "externalDocs:{description:[],url:'',value:[]},"
+				+ "name:'name',"
+				+ "value:['value']"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Tag(
+		description="description",
+		externalDocs=@ExternalDocs,
+		name="name",
+		value="value"
+	)
+	public static class D1 {}
+	Tag d1 = D1.class.getAnnotationsByType(Tag.class)[0];
+
+	@Tag(
+		description="description",
+		externalDocs=@ExternalDocs,
+		name="name",
+		value="value"
+	)
+	public static class D2 {}
+	Tag d2 = D2.class.getAnnotationsByType(Tag.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/jena/annotation/RdfBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/jena/annotation/RdfBuilder_Test.java
new file mode 100644
index 0000000..670a888
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/jena/annotation/RdfBuilder_Test.java
@@ -0,0 +1,135 @@
+// ***************************************************************************************************************************
+// * 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.jena.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.jena.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class RdfBuilder_Test {
+
+	private static final String CNAME = RdfBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Rdf a1 = RdfBuilder.create()
+		.beanUri(true)
+		.collectionFormat(RdfCollectionFormat.BAG)
+		.namespace("c")
+		.on("d")
+		.prefix("e")
+		.build();
+
+	Rdf a2 = RdfBuilder.create()
+		.beanUri(true)
+		.collectionFormat(RdfCollectionFormat.BAG)
+		.namespace("c")
+		.on("d")
+		.prefix("e")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).stderr().json().is(""
+			+ "{"
+				+ "beanUri:true,"
+				+ "collectionFormat:'BAG',"
+				+ "namespace:'c',"
+				+ "on:['d'],"
+				+ "onClass:[],"
+				+ "prefix:'e'"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Rdf c1 = RdfBuilder.create(C1.class).on(C2.class).build();
+		Rdf c2 = RdfBuilder.create("a").on("b").build();
+		Rdf c3 = RdfBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		Rdf c4 = RdfBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Rdf(
+		beanUri=true,
+		collectionFormat=RdfCollectionFormat.BAG,
+		namespace="c",
+		on="d",
+		prefix="e"
+	)
+	public static class D1 {}
+	Rdf d1 = D1.class.getAnnotationsByType(Rdf.class)[0];
+
+	@Rdf(
+		beanUri=true,
+		collectionFormat=RdfCollectionFormat.BAG,
+		namespace="c",
+		on="d",
+		prefix="e"
+	)
+	public static class D2 {}
+	Rdf d2 = D2.class.getAnnotationsByType(Rdf.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/jso/annotation/JsoBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/jso/annotation/JsoBuilder_Test.java
new file mode 100644
index 0000000..66c6982
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/jso/annotation/JsoBuilder_Test.java
@@ -0,0 +1,114 @@
+// ***************************************************************************************************************************
+// * 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.jso.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class JsoBuilder_Test {
+
+	private static final String CNAME = JsoBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Jso a1 = JsoBuilder.create()
+		.on("a")
+		.build();
+
+	Jso a2 = JsoBuilder.create()
+		.on("a")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "on:['a'],"
+				+ "onClass:[]"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Jso c1 = JsoBuilder.create(C1.class).on(C2.class).build();
+		Jso c2 = JsoBuilder.create("a").on("b").build();
+		Jso c3 = JsoBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		Jso c4 = JsoBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Jso(
+		on="a"
+	)
+	public static class D1 {}
+	Jso d1 = D1.class.getAnnotationsByType(Jso.class)[0];
+
+	@Jso(
+		on="a"
+	)
+	public static class D2 {}
+	Jso d2 = D2.class.getAnnotationsByType(Jso.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/json/annotation/JsonBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/json/annotation/JsonBuilder_Test.java
new file mode 100644
index 0000000..1bbd00f
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/json/annotation/JsonBuilder_Test.java
@@ -0,0 +1,119 @@
+// ***************************************************************************************************************************
+// * 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.json.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class JsonBuilder_Test {
+
+	private static final String CNAME = JsonBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Json a1 = JsonBuilder.create()
+		.on("a")
+		.wrapperAttr("b")
+		.build();
+
+	Json a2 = JsonBuilder.create()
+		.on("a")
+		.wrapperAttr("b")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "on:['a'],"
+				+ "onClass:[],"
+				+ "wrapperAttr:'b'"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Json c1 = JsonBuilder.create(C1.class).on(C2.class).build();
+		Json c2 = JsonBuilder.create("a").on("b").build();
+		Json c3 = JsonBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		Json c4 = JsonBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Json(
+		on="a",
+		wrapperAttr="b"
+	)
+	public static class D1 {}
+	Json d1 = D1.class.getAnnotationsByType(Json.class)[0];
+
+	@Json(
+		on="a",
+		wrapperAttr="b"
+	)
+	public static class D2 {}
+	Json d2 = D2.class.getAnnotationsByType(Json.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/jsonschema/annotation/ExternalDocsBuilder_Test.java
similarity index 51%
rename from juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreTest.java
rename to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/jsonschema/annotation/ExternalDocsBuilder_Test.java
index fe34da0..7cacf63 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/jsonschema/annotation/ExternalDocsBuilder_Test.java
@@ -10,116 +10,90 @@
 // * "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.annotation;
+package org.apache.juneau.jsonschema.annotation;
 
 import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
 import static org.junit.runners.MethodSorters.*;
 
-import org.apache.juneau.json.*;
+import org.apache.juneau.*;
 import org.junit.*;
 
 @FixMethodOrder(NAME_ASCENDING)
-public class BeanIgnoreTest {
+public class ExternalDocsBuilder_Test {
 
 	//------------------------------------------------------------------------------------------------------------------
-	// Test @BeanIgnore on properties
+	// Basic tests
 	//------------------------------------------------------------------------------------------------------------------
 
-	public static class A {
-		public String getA() {
-			return "a";
-		}
+	ExternalDocs a1 = ExternalDocsBuilder.create()
+		.description("description")
+		.url("url")
+		.value("value")
+		.build();
 
-		@BeanIgnore
-		public String getB() {
-			return "b";
-		}
-
-		public String c = "c";
-
-		@BeanIgnore public String d = "d";
-	}
+	ExternalDocs a2 = ExternalDocsBuilder.create()
+		.description("description")
+		.url("url")
+		.value("value")
+		.build();
 
 	@Test
-	public void testBeanIgnoreOnProperties() throws Exception {
-		assertObject(new A()).json().is("{c:'c',a:'a'}");
-	}
-
-	@BeanConfig(
-		applyBeanIgnore={
-			@BeanIgnore(on="Ac.getB"),
-			@BeanIgnore(on="Ac.d")
-		}
-	)
-	public static class Ac {
-		public String getA() {
-			return "a";
-		}
-
-		public String getB() {
-			return "b";
-		}
-
-		public String c = "c";
-
-		public String d = "d";
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "description:['description'],"
+				+ "url:'url',"
+				+ "value:['value']"
+			+ "}"
+		);
 	}
 
 	@Test
-	public void testBeanIgnoreOnProperties_usingConfig() throws Exception {
-		assertObject(new Ac()).serialized(SimpleJsonSerializer.DEFAULT.builder().applyAnnotations(Ac.class).build()).is("{c:'c',a:'a'}");
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
 	}
 
 	//------------------------------------------------------------------------------------------------------------------
-	// Test @BeanIgnore on classes
+	// PropertyStore equivalency.
 	//------------------------------------------------------------------------------------------------------------------
 
-	@BeanIgnore
-	public static class B1 {
-		public int f = 1;
-
-		@Override
-		public String toString() {
-			return "xxx";
-		}
-	}
-
-	public static class B {
-		public int f2 = 2;
-		public B1 f3 = new B1();
-
-		public B1 getF4() {
-			return new B1();
-		}
-	}
-
 	@Test
-	public void testBeanIgnoreOnBean() throws Exception {
-		assertObject(new B()).json().is("{f2:2,f3:'xxx',f4:'xxx'}");
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
 	}
 
-	@BeanConfig(applyBeanIgnore=@BeanIgnore(on="B1c"))
-	public static class B1c {
-		public int f = 1;
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
 
-		@Override
-		public String toString() {
-			return "xxx";
-		}
-	}
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
 
-	public static class Bc {
-		public int f2 = 2;
-		public B1c f3 = new B1c();
+	@ExternalDocs(
+		description="description",
+		url="url",
+		value="value"
+	)
+	public static class D1 {}
+	ExternalDocs d1 = D1.class.getAnnotationsByType(ExternalDocs.class)[0];
 
-		public B1c getF4() {
-			return new B1c();
-		}
-	}
+	@ExternalDocs(
+		description="description",
+		url="url",
+		value="value"
+	)
+	public static class D2 {}
+	ExternalDocs d2 = D2.class.getAnnotationsByType(ExternalDocs.class)[0];
 
 	@Test
-	public void testBeanIgnoreOnBean_usingConfig() throws Exception {
-		assertObject(new Bc()).serialized(SimpleJsonSerializer.DEFAULT.builder().applyAnnotations(B1c.class).build()).is("{f2:2,f3:'xxx',f4:'xxx'}");
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
 	}
 }
 
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/jsonschema/annotation/ItemsBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/jsonschema/annotation/ItemsBuilder_Test.java
new file mode 100644
index 0000000..60f5254
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/jsonschema/annotation/ItemsBuilder_Test.java
@@ -0,0 +1,255 @@
+// ***************************************************************************************************************************
+// * 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.jsonschema.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class ItemsBuilder_Test {
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Items a1 = ItemsBuilder.create()
+		.$ref("$ref")
+		._default("default")
+		._enum("enum")
+		.cf("cf")
+		.collectionFormat("collectionFormat")
+		.df("df")
+		.e("e")
+		.emax(true)
+		.emin(true)
+		.exclusiveMaximum(true)
+		.exclusiveMinimum(true)
+		.f("f")
+		.format("format")
+		.max("max")
+		.maxi(1)
+		.maximum("maximum")
+		.maxItems(2)
+		.maxl(3)
+		.maxLength(4)
+		.min("min")
+		.mini(5)
+		.minimum("minimum")
+		.minItems(6)
+		.minl(7)
+		.minLength(8)
+		.mo("mo")
+		.multipleOf("multipleOf")
+		.p("p")
+		.pattern("pattern")
+		.t("t")
+		.type("type")
+		.ui(true)
+		.uniqueItems(true)
+		.value("value")
+		.build();
+
+	Items a2 = ItemsBuilder.create()
+		.$ref("$ref")
+		._default("default")
+		._enum("enum")
+		.cf("cf")
+		.collectionFormat("collectionFormat")
+		.df("df")
+		.e("e")
+		.emax(true)
+		.emin(true)
+		.exclusiveMaximum(true)
+		.exclusiveMinimum(true)
+		.f("f")
+		.format("format")
+		.max("max")
+		.maxi(1)
+		.maximum("maximum")
+		.maxItems(2)
+		.maxl(3)
+		.maxLength(4)
+		.min("min")
+		.mini(5)
+		.minimum("minimum")
+		.minItems(6)
+		.minl(7)
+		.minLength(8)
+		.mo("mo")
+		.multipleOf("multipleOf")
+		.p("p")
+		.pattern("pattern")
+		.t("t")
+		.type("type")
+		.ui(true)
+		.uniqueItems(true)
+		.value("value")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).stderr().json().is(""
+			+ "{"
+				+ "'$ref':'$ref',"
+				+ "_default:['default'],"
+				+ "_enum:['enum'],"
+				+ "cf:'cf',"
+				+ "collectionFormat:'collectionFormat',"
+				+ "df:['df'],"
+				+ "e:['e'],"
+				+ "emax:true,"
+				+ "emin:true,"
+				+ "exclusiveMaximum:true,"
+				+ "exclusiveMinimum:true,"
+				+ "f:'f',"
+				+ "format:'format',"
+				+ "items:{'$ref':'',_default:[],_enum:[],cf:'',collectionFormat:'',df:[],e:[],emax:false,emin:false,exclusiveMaximum:false,exclusiveMinimum:false,f:'',format:'',items:[],max:'',maxItems:-1,maxLength:-1,maxi:-1,maximum:'',maxl:-1,min:'',minItems:-1,minLength:-1,mini:-1,minimum:'',minl:-1,mo:'',multipleOf:'',p:'',pattern:'',t:'',type:'',ui:false,uniqueItems:false,value:[]},"
+				+ "max:'max',"
+				+ "maxItems:2,"
+				+ "maxLength:4,"
+				+ "maxi:1,"
+				+ "maximum:'maximum',"
+				+ "maxl:3,"
+				+ "min:'min',"
+				+ "minItems:6,"
+				+ "minLength:8,"
+				+ "mini:5,"
+				+ "minimum:'minimum',"
+				+ "minl:7,"
+				+ "mo:'mo',"
+				+ "multipleOf:'multipleOf',"
+				+ "p:'p',"
+				+ "pattern:'pattern',"
+				+ "t:'t',"
+				+ "type:'type',"
+				+ "ui:true,"
+				+ "uniqueItems:true,"
+				+ "value:['value']"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Items(
+		$ref="$ref",
+		_default="default",
+		_enum="enum",
+		cf="cf",
+		collectionFormat="collectionFormat",
+		df="df",
+		e="e",
+		emax=true,
+		emin=true,
+		exclusiveMaximum=true,
+		exclusiveMinimum=true,
+		f="f",
+		format="format",
+		max="max",
+		maxi=1,
+		maximum="maximum",
+		maxItems=2,
+		maxl=3,
+		maxLength=4,
+		min="min",
+		mini=5,
+		minimum="minimum",
+		minItems=6,
+		minl=7,
+		minLength=8,
+		mo="mo",
+		multipleOf="multipleOf",
+		p="p",
+		pattern="pattern",
+		t="t",
+		type="type",
+		ui=true,
+		uniqueItems=true,
+		value="value"
+	)
+	public static class D1 {}
+	Items d1 = D1.class.getAnnotationsByType(Items.class)[0];
+
+	@Items(
+		$ref="$ref",
+		_default="default",
+		_enum="enum",
+		cf="cf",
+		collectionFormat="collectionFormat",
+		df="df",
+		e="e",
+		emax=true,
+		emin=true,
+		exclusiveMaximum=true,
+		exclusiveMinimum=true,
+		f="f",
+		format="format",
+		max="max",
+		maxi=1,
+		maximum="maximum",
+		maxItems=2,
+		maxl=3,
+		maxLength=4,
+		min="min",
+		mini=5,
+		minimum="minimum",
+		minItems=6,
+		minl=7,
+		minLength=8,
+		mo="mo",
+		multipleOf="multipleOf",
+		p="p",
+		pattern="pattern",
+		t="t",
+		type="type",
+		ui=true,
+		uniqueItems=true,
+		value="value"
+	)
+	public static class D2 {}
+	Items d2 = D2.class.getAnnotationsByType(Items.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/jsonschema/annotation/SchemaBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/jsonschema/annotation/SchemaBuilder_Test.java
new file mode 100644
index 0000000..2dc480e
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/jsonschema/annotation/SchemaBuilder_Test.java
@@ -0,0 +1,405 @@
+// ***************************************************************************************************************************
+// * 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.jsonschema.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class SchemaBuilder_Test {
+
+	private static final String CNAME = SchemaBuilder_Test.class.getName();
+
+	private static class X1 {}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Schema a1 = SchemaBuilder.create()
+		._default("default")
+		._enum("enum")
+		.$ref("$ref")
+		.additionalProperties("additionalProperties")
+		.allOf("allOf")
+		.cf("cf")
+		.collectionFormat("collectionFormat")
+		.d("d")
+		.description("description")
+		.df("df")
+		.discriminator("discriminator")
+		.e("e")
+		.emax(true)
+		.emin(true)
+		.ex("ex")
+		.example("example")
+		.examples("examples")
+		.exclusiveMaximum(true)
+		.exclusiveMinimum(true)
+		.exs("exs")
+		.externalDocs(ExternalDocsBuilder.DEFAULT)
+		.f("f")
+		.format("format")
+		.ignore(true)
+		.items(ItemsBuilder.DEFAULT)
+		.max("max")
+		.maxi(1)
+		.maximum("maximum")
+		.maxItems(2)
+		.maxl(3)
+		.maxLength(4)
+		.maxp(5)
+		.maxProperties(6)
+		.min("min")
+		.mini(7)
+		.minimum("minimum")
+		.minItems(8)
+		.minl(9)
+		.minLength(10)
+		.minp(11)
+		.minProperties(12)
+		.mo("mo")
+		.multipleOf("multipleOf")
+		.on("on")
+		.onClass(X1.class)
+		.p("p")
+		.pattern("pattern")
+		.properties("properties")
+		.r(true)
+		.readOnly(true)
+		.required(true)
+		.ro(true)
+		.t("t")
+		.title("title")
+		.type("type")
+		.ui(true)
+		.uniqueItems(true)
+		.value("value")
+		.xml("xml")
+		.build();
+
+	Schema a2 = SchemaBuilder.create()
+		._default("default")
+		._enum("enum")
+		.$ref("$ref")
+		.additionalProperties("additionalProperties")
+		.allOf("allOf")
+		.cf("cf")
+		.collectionFormat("collectionFormat")
+		.d("d")
+		.description("description")
+		.df("df")
+		.discriminator("discriminator")
+		.e("e")
+		.emax(true)
+		.emin(true)
+		.ex("ex")
+		.example("example")
+		.examples("examples")
+		.exclusiveMaximum(true)
+		.exclusiveMinimum(true)
+		.exs("exs")
+		.externalDocs(ExternalDocsBuilder.DEFAULT)
+		.f("f")
+		.format("format")
+		.ignore(true)
+		.items(ItemsBuilder.DEFAULT)
+		.max("max")
+		.maxi(1)
+		.maximum("maximum")
+		.maxItems(2)
+		.maxl(3)
+		.maxLength(4)
+		.maxp(5)
+		.maxProperties(6)
+		.min("min")
+		.mini(7)
+		.minimum("minimum")
+		.minItems(8)
+		.minl(9)
+		.minLength(10)
+		.minp(11)
+		.minProperties(12)
+		.mo("mo")
+		.multipleOf("multipleOf")
+		.on("on")
+		.onClass(X1.class)
+		.p("p")
+		.pattern("pattern")
+		.properties("properties")
+		.r(true)
+		.readOnly(true)
+		.required(true)
+		.ro(true)
+		.t("t")
+		.title("title")
+		.type("type")
+		.ui(true)
+		.uniqueItems(true)
+		.value("value")
+		.xml("xml")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).stderr().json().is(""
+			+ "{"
+				+ "'$ref':'$ref',"
+				+ "_default:['default'],"
+				+ "_enum:['enum'],"
+				+ "additionalProperties:['additionalProperties'],"
+				+ "allOf:['allOf'],"
+				+ "cf:'cf',"
+				+ "collectionFormat:'collectionFormat',"
+				+ "d:['d'],"
+				+ "description:['description'],"
+				+ "df:['df'],"
+				+ "discriminator:'discriminator',"
+				+ "e:['e'],"
+				+ "emax:true,"
+				+ "emin:true,"
+				+ "ex:['ex'],"
+				+ "example:['example'],"
+				+ "examples:['examples'],"
+				+ "exclusiveMaximum:true,"
+				+ "exclusiveMinimum:true,"
+				+ "exs:['exs'],"
+				+ "externalDocs:{description:[],url:'',value:[]},"
+				+ "f:'f',"
+				+ "format:'format',"
+				+ "ignore:true,"
+				+ "items:{'$ref':'',_default:[],_enum:[],cf:'',collectionFormat:'',df:[],e:[],emax:false,emin:false,exclusiveMaximum:false,exclusiveMinimum:false,f:'',format:'',items:{'$ref':'',_default:[],_enum:[],cf:'',collectionFormat:'',df:[],e:[],emax:false,emin:false,exclusiveMaximum:false,exclusiveMinimum:false,f:'',format:'',items:[],max:'',maxItems:-1,maxLength:-1,maxi:-1,maximum:'',maxl:-1,min:'',minItems:-1,minLength:-1,mini:-1,minimum:'',minl:-1,mo:'',multipleOf:'',p:'',pattern:'',t:'',t [...]
+				+ "max:'max',"
+				+ "maxItems:2,"
+				+ "maxLength:4,"
+				+ "maxProperties:6,"
+				+ "maxi:1,"
+				+ "maximum:'maximum',"
+				+ "maxl:3,"
+				+ "maxp:5,"
+				+ "min:'min',"
+				+ "minItems:8,"
+				+ "minLength:10,"
+				+ "minProperties:12,"
+				+ "mini:7,"
+				+ "minimum:'minimum',"
+				+ "minl:9,"
+				+ "minp:11,"
+				+ "mo:'mo',"
+				+ "multipleOf:'multipleOf',"
+				+ "on:['on'],"
+				+ "onClass:['"+CNAME+"$X1'],"
+				+ "p:'p',"
+				+ "pattern:'pattern',"
+				+ "properties:['properties'],"
+				+ "r:true,"
+				+ "readOnly:true,"
+				+ "required:true,"
+				+ "ro:true,"
+				+ "t:'t',"
+				+ "title:'title',"
+				+ "type:'type',"
+				+ "ui:true,"
+				+ "uniqueItems:true,"
+				+ "value:['value'],"
+				+ "xml:['xml']"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Schema c1 = SchemaBuilder.create(C1.class).on(C2.class).build();
+		Schema c2 = SchemaBuilder.create("a").on("b").build();
+		Schema c3 = SchemaBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		Schema c4 = SchemaBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Schema(
+		_default="default",
+		_enum="enum",
+		$ref="$ref",
+		additionalProperties="additionalProperties",
+		allOf="allOf",
+		cf="cf",
+		collectionFormat="collectionFormat",
+		d="d",
+		description="description",
+		df="df",
+		discriminator="discriminator",
+		e="e",
+		emax=true,
+		emin=true,
+		ex="ex",
+		example="example",
+		examples="examples",
+		exclusiveMaximum=true,
+		exclusiveMinimum=true,
+		exs="exs",
+		externalDocs=@ExternalDocs,
+		f="f",
+		format="format",
+		ignore=true,
+		items=@Items,
+		max="max",
+		maxi=1,
+		maximum="maximum",
+		maxItems=2,
+		maxl=3,
+		maxLength=4,
+		maxp=5,
+		maxProperties=6,
+		min="min",
+		mini=7,
+		minimum="minimum",
+		minItems=8,
+		minl=9,
+		minLength=10,
+		minp=11,
+		minProperties=12,
+		mo="mo",
+		multipleOf="multipleOf",
+		on="on",
+		onClass=X1.class,
+		p="p",
+		pattern="pattern",
+		properties="properties",
+		r=true,
+		readOnly=true,
+		required=true,
+		ro=true,
+		t="t",
+		title="title",
+		type="type",
+		ui=true,
+		uniqueItems=true,
+		value="value",
+		xml="xml"
+	)
+	public static class D1 {}
+	Schema d1 = D1.class.getAnnotationsByType(Schema.class)[0];
+
+	@Schema(
+		_default="default",
+		_enum="enum",
+		$ref="$ref",
+		additionalProperties="additionalProperties",
+		allOf="allOf",
+		cf="cf",
+		collectionFormat="collectionFormat",
+		d="d",
+		description="description",
+		df="df",
+		discriminator="discriminator",
+		e="e",
+		emax=true,
+		emin=true,
+		ex="ex",
+		example="example",
+		examples="examples",
+		exclusiveMaximum=true,
+		exclusiveMinimum=true,
+		exs="exs",
+		externalDocs=@ExternalDocs,
+		f="f",
+		format="format",
+		ignore=true,
+		items=@Items,
+		max="max",
+		maxi=1,
+		maximum="maximum",
+		maxItems=2,
+		maxl=3,
+		maxLength=4,
+		maxp=5,
+		maxProperties=6,
+		min="min",
+		mini=7,
+		minimum="minimum",
+		minItems=8,
+		minl=9,
+		minLength=10,
+		minp=11,
+		minProperties=12,
+		mo="mo",
+		multipleOf="multipleOf",
+		on="on",
+		onClass=X1.class,
+		p="p",
+		pattern="pattern",
+		properties="properties",
+		r=true,
+		readOnly=true,
+		required=true,
+		ro=true,
+		t="t",
+		title="title",
+		type="type",
+		ui=true,
+		uniqueItems=true,
+		value="value",
+		xml="xml"
+	)
+	public static class D2 {}
+	Schema d2 = D2.class.getAnnotationsByType(Schema.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).stderr().is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/jsonschema/annotation/SubItemsBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/jsonschema/annotation/SubItemsBuilder_Test.java
new file mode 100644
index 0000000..aca7106
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/jsonschema/annotation/SubItemsBuilder_Test.java
@@ -0,0 +1,255 @@
+// ***************************************************************************************************************************
+// * 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.jsonschema.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class SubItemsBuilder_Test {
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	SubItems a1 = SubItemsBuilder.create()
+		.$ref("$ref")
+		._default("default")
+		._enum("enum")
+		.cf("cf")
+		.collectionFormat("collectionFormat")
+		.df("df")
+		.e("e")
+		.emax(true)
+		.emin(true)
+		.exclusiveMaximum(true)
+		.exclusiveMinimum(true)
+		.f("f")
+		.format("format")
+		.max("max")
+		.maxi(1)
+		.maximum("maximum")
+		.maxItems(2)
+		.maxl(3)
+		.maxLength(4)
+		.min("min")
+		.mini(5)
+		.minimum("minimum")
+		.minItems(6)
+		.minl(7)
+		.minLength(8)
+		.mo("mo")
+		.multipleOf("multipleOf")
+		.p("p")
+		.pattern("pattern")
+		.t("t")
+		.type("type")
+		.ui(true)
+		.uniqueItems(true)
+		.value("value")
+		.build();
+
+	SubItems a2 = SubItemsBuilder.create()
+		.$ref("$ref")
+		._default("default")
+		._enum("enum")
+		.cf("cf")
+		.collectionFormat("collectionFormat")
+		.df("df")
+		.e("e")
+		.emax(true)
+		.emin(true)
+		.exclusiveMaximum(true)
+		.exclusiveMinimum(true)
+		.f("f")
+		.format("format")
+		.max("max")
+		.maxi(1)
+		.maximum("maximum")
+		.maxItems(2)
+		.maxl(3)
+		.maxLength(4)
+		.min("min")
+		.mini(5)
+		.minimum("minimum")
+		.minItems(6)
+		.minl(7)
+		.minLength(8)
+		.mo("mo")
+		.multipleOf("multipleOf")
+		.p("p")
+		.pattern("pattern")
+		.t("t")
+		.type("type")
+		.ui(true)
+		.uniqueItems(true)
+		.value("value")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).stderr().json().is(""
+			+ "{"
+				+ "'$ref':'$ref',"
+				+ "_default:['default'],"
+				+ "_enum:['enum'],"
+				+ "cf:'cf',"
+				+ "collectionFormat:'collectionFormat',"
+				+ "df:['df'],"
+				+ "e:['e'],"
+				+ "emax:true,"
+				+ "emin:true,"
+				+ "exclusiveMaximum:true,"
+				+ "exclusiveMinimum:true,"
+				+ "f:'f',"
+				+ "format:'format',"
+				+ "items:[],"
+				+ "max:'max',"
+				+ "maxItems:2,"
+				+ "maxLength:4,"
+				+ "maxi:1,"
+				+ "maximum:'maximum',"
+				+ "maxl:3,"
+				+ "min:'min',"
+				+ "minItems:6,"
+				+ "minLength:8,"
+				+ "mini:5,"
+				+ "minimum:'minimum',"
+				+ "minl:7,"
+				+ "mo:'mo',"
+				+ "multipleOf:'multipleOf',"
+				+ "p:'p',"
+				+ "pattern:'pattern',"
+				+ "t:'t',"
+				+ "type:'type',"
+				+ "ui:true,"
+				+ "uniqueItems:true,"
+				+ "value:['value']"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@SubItems(
+		$ref="$ref",
+		_default="default",
+		_enum="enum",
+		cf="cf",
+		collectionFormat="collectionFormat",
+		df="df",
+		e="e",
+		emax=true,
+		emin=true,
+		exclusiveMaximum=true,
+		exclusiveMinimum=true,
+		f="f",
+		format="format",
+		max="max",
+		maxi=1,
+		maximum="maximum",
+		maxItems=2,
+		maxl=3,
+		maxLength=4,
+		min="min",
+		mini=5,
+		minimum="minimum",
+		minItems=6,
+		minl=7,
+		minLength=8,
+		mo="mo",
+		multipleOf="multipleOf",
+		p="p",
+		pattern="pattern",
+		t="t",
+		type="type",
+		ui=true,
+		uniqueItems=true,
+		value="value"
+	)
+	public static class D1 {}
+	SubItems d1 = D1.class.getAnnotationsByType(SubItems.class)[0];
+
+	@SubItems(
+		$ref="$ref",
+		_default="default",
+		_enum="enum",
+		cf="cf",
+		collectionFormat="collectionFormat",
+		df="df",
+		e="e",
+		emax=true,
+		emin=true,
+		exclusiveMaximum=true,
+		exclusiveMinimum=true,
+		f="f",
+		format="format",
+		max="max",
+		maxi=1,
+		maximum="maximum",
+		maxItems=2,
+		maxl=3,
+		maxLength=4,
+		min="min",
+		mini=5,
+		minimum="minimum",
+		minItems=6,
+		minl=7,
+		minLength=8,
+		mo="mo",
+		multipleOf="multipleOf",
+		p="p",
+		pattern="pattern",
+		t="t",
+		type="type",
+		ui=true,
+		uniqueItems=true,
+		value="value"
+	)
+	public static class D2 {}
+	SubItems d2 = D2.class.getAnnotationsByType(SubItems.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/msgpack/annotation/MsgPackBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/msgpack/annotation/MsgPackBuilder_Test.java
new file mode 100644
index 0000000..090e1a5
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/msgpack/annotation/MsgPackBuilder_Test.java
@@ -0,0 +1,114 @@
+// ***************************************************************************************************************************
+// * 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.msgpack.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class MsgPackBuilder_Test {
+
+	private static final String CNAME = MsgPackBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	MsgPack a1 = MsgPackBuilder.create()
+		.on("a")
+		.build();
+
+	MsgPack a2 = MsgPackBuilder.create()
+		.on("a")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "on:['a'],"
+				+ "onClass:[]"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		MsgPack c1 = MsgPackBuilder.create(C1.class).on(C2.class).build();
+		MsgPack c2 = MsgPackBuilder.create("a").on("b").build();
+		MsgPack c3 = MsgPackBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		MsgPack c4 = MsgPackBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@MsgPack(
+		on="a"
+	)
+	public static class D1 {}
+	MsgPack d1 = D1.class.getAnnotationsByType(MsgPack.class)[0];
+
+	@MsgPack(
+		on="a"
+	)
+	public static class D2 {}
+	MsgPack d2 = D2.class.getAnnotationsByType(MsgPack.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/oapi/annotation/OpenApiBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/oapi/annotation/OpenApiBuilder_Test.java
new file mode 100644
index 0000000..dd5151b
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/oapi/annotation/OpenApiBuilder_Test.java
@@ -0,0 +1,114 @@
+// ***************************************************************************************************************************
+// * 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.oapi.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class OpenApiBuilder_Test {
+
+	private static final String CNAME = OpenApiBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	OpenApi a1 = OpenApiBuilder.create()
+		.on("a")
+		.build();
+
+	OpenApi a2 = OpenApiBuilder.create()
+		.on("a")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "on:['a'],"
+				+ "onClass:[]"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		OpenApi c1 = OpenApiBuilder.create(C1.class).on(C2.class).build();
+		OpenApi c2 = OpenApiBuilder.create("a").on("b").build();
+		OpenApi c3 = OpenApiBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		OpenApi c4 = OpenApiBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@OpenApi(
+		on="a"
+	)
+	public static class D1 {}
+	OpenApi d1 = D1.class.getAnnotationsByType(OpenApi.class)[0];
+
+	@OpenApi(
+		on="a"
+	)
+	public static class D2 {}
+	OpenApi d2 = D2.class.getAnnotationsByType(OpenApi.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/plaintext/annotation/PlainTextBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/plaintext/annotation/PlainTextBuilder_Test.java
new file mode 100644
index 0000000..9292288
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/plaintext/annotation/PlainTextBuilder_Test.java
@@ -0,0 +1,114 @@
+// ***************************************************************************************************************************
+// * 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.plaintext.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class PlainTextBuilder_Test {
+
+	private static final String CNAME = PlainTextBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	PlainText a1 = PlainTextBuilder.create()
+		.on("a")
+		.build();
+
+	PlainText a2 = PlainTextBuilder.create()
+		.on("a")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "on:['a'],"
+				+ "onClass:[]"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		PlainText c1 = PlainTextBuilder.create(C1.class).on(C2.class).build();
+		PlainText c2 = PlainTextBuilder.create("a").on("b").build();
+		PlainText c3 = PlainTextBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		PlainText c4 = PlainTextBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@PlainText(
+		on="a"
+	)
+	public static class D1 {}
+	PlainText d1 = D1.class.getAnnotationsByType(PlainText.class)[0];
+
+	@PlainText(
+		on="a"
+	)
+	public static class D2 {}
+	PlainText d2 = D2.class.getAnnotationsByType(PlainText.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/soap/annotation/SoapXmlBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/soap/annotation/SoapXmlBuilder_Test.java
new file mode 100644
index 0000000..6eb3e38
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/soap/annotation/SoapXmlBuilder_Test.java
@@ -0,0 +1,114 @@
+// ***************************************************************************************************************************
+// * 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.soap.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class SoapXmlBuilder_Test {
+
+	private static final String CNAME = SoapXmlBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	SoapXml a1 = SoapXmlBuilder.create()
+		.on("a")
+		.build();
+
+	SoapXml a2 = SoapXmlBuilder.create()
+		.on("a")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "on:['a'],"
+				+ "onClass:[]"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		SoapXml c1 = SoapXmlBuilder.create(C1.class).on(C2.class).build();
+		SoapXml c2 = SoapXmlBuilder.create("a").on("b").build();
+		SoapXml c3 = SoapXmlBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		SoapXml c4 = SoapXmlBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@SoapXml(
+		on="a"
+	)
+	public static class D1 {}
+	SoapXml d1 = D1.class.getAnnotationsByType(SoapXml.class)[0];
+
+	@SoapXml(
+		on="a"
+	)
+	public static class D2 {}
+	SoapXml d2 = D2.class.getAnnotationsByType(SoapXml.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/transforms/SwapsAnnotationComboTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/transforms/SwapsAnnotationComboTest.java
index a4a8a3e..4dc0344 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/transforms/SwapsAnnotationComboTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/transforms/SwapsAnnotationComboTest.java
@@ -507,65 +507,45 @@ public class SwapsAnnotationComboTest extends ComboSerializeTest {
 			).build();
 	}
 
-	@Swaps(
-		{
-			@Swap(value=SwapJson.class, mediaTypes={"application/json"}),
-			@Swap(value=SwapXml.class, mediaTypes={"text/xml"}),
-			@Swap(value=SwapHtml.class, mediaTypes={"text/html"}),
-			@Swap(value=SwapUon.class, mediaTypes={"text/uon"}),
-			@Swap(value=SwapUrlEncoding.class, mediaTypes={"application/x-www-form-urlencoded"}),
-			@Swap(value=SwapMsgPack.class, mediaTypes={"octal/msgpack"}),
-			@Swap(value=SwapRdfXml.class, mediaTypes={"text/xml+rdf"}),
-		}
-	)
+	@Swap(value=SwapJson.class, mediaTypes={"application/json"})
+	@Swap(value=SwapXml.class, mediaTypes={"text/xml"})
+	@Swap(value=SwapHtml.class, mediaTypes={"text/html"})
+	@Swap(value=SwapUon.class, mediaTypes={"text/uon"})
+	@Swap(value=SwapUrlEncoding.class, mediaTypes={"application/x-www-form-urlencoded"})
+	@Swap(value=SwapMsgPack.class, mediaTypes={"octal/msgpack"})
+	@Swap(value=SwapRdfXml.class, mediaTypes={"text/xml+rdf"})
 	public static class TestMediaTypeLiterals {}
 
-	@Swaps(
-		{
-			@Swap(value=SwapJson.class, mediaTypes={"*/json"}),
-			@Swap(value=SwapXml.class, mediaTypes={"*/xml"}),
-			@Swap(value=SwapHtml.class, mediaTypes={"*/html"}),
-			@Swap(value=SwapUon.class, mediaTypes={"*/uon"}),
-			@Swap(value=SwapUrlEncoding.class, mediaTypes={"*/x-www-form-urlencoded"}),
-			@Swap(value=SwapMsgPack.class, mediaTypes={"*/msgpack"}),
-			@Swap(value=SwapRdfXml.class, mediaTypes={"*/xml+rdf"}),
-		}
-	)
+	@Swap(value=SwapJson.class, mediaTypes={"*/json"})
+	@Swap(value=SwapXml.class, mediaTypes={"*/xml"})
+	@Swap(value=SwapHtml.class, mediaTypes={"*/html"})
+	@Swap(value=SwapUon.class, mediaTypes={"*/uon"})
+	@Swap(value=SwapUrlEncoding.class, mediaTypes={"*/x-www-form-urlencoded"})
+	@Swap(value=SwapMsgPack.class, mediaTypes={"*/msgpack"})
+	@Swap(value=SwapRdfXml.class, mediaTypes={"*/xml+rdf"})
 	public static class TestMediaTypePatterns {}
 
-	@Swaps(
-		{
-			@Swap(value=SwapRdfXml.class, mediaTypes={"*/xml+rdf"}),
-			@Swap(value=SwapMsgPack.class, mediaTypes={"*/msgpack"}),
-			@Swap(value=SwapUrlEncoding.class, mediaTypes={"*/x-www-form-urlencoded"}),
-			@Swap(value=SwapUon.class, mediaTypes={"*/uon"}),
-			@Swap(value=SwapHtml.class, mediaTypes={"*/html"}),
-			@Swap(value=SwapXml.class, mediaTypes={"*/xml"}),
-			@Swap(value=SwapJson.class, mediaTypes={"*/json"}),
-		}
-	)
+	@Swap(value=SwapRdfXml.class, mediaTypes={"*/xml+rdf"})
+	@Swap(value=SwapMsgPack.class, mediaTypes={"*/msgpack"})
+	@Swap(value=SwapUrlEncoding.class, mediaTypes={"*/x-www-form-urlencoded"})
+	@Swap(value=SwapUon.class, mediaTypes={"*/uon"})
+	@Swap(value=SwapHtml.class, mediaTypes={"*/html"})
+	@Swap(value=SwapXml.class, mediaTypes={"*/xml"})
+	@Swap(value=SwapJson.class, mediaTypes={"*/json"})
 	public static class TestMediaTypePatternsReversed {}
 
-	@Swaps(
-		{
-			@Swap(value=SwapJson.class, mediaTypes={"*/foo","*/json","*/bar"}),
-			@Swap(value=SwapXml.class, mediaTypes={"*/foo","*/xml","*/bar"}),
-			@Swap(value=SwapHtml.class, mediaTypes={"*/foo","*/html","*/bar"}),
-			@Swap(value=SwapUon.class, mediaTypes={"*/foo","*/uon","*/bar"}),
-			@Swap(value=SwapUrlEncoding.class, mediaTypes={"*/foo","*/x-www-form-urlencoded","*/bar"}),
-			@Swap(value=SwapMsgPack.class, mediaTypes={"*/foo","*/msgpack","*/bar"}),
-			@Swap(value=SwapRdfXml.class, mediaTypes={"*/foo","*/xml+rdf","*/bar"}),
-		}
-	)
+	@Swap(value=SwapJson.class, mediaTypes={"*/foo","*/json","*/bar"})
+	@Swap(value=SwapXml.class, mediaTypes={"*/foo","*/xml","*/bar"})
+	@Swap(value=SwapHtml.class, mediaTypes={"*/foo","*/html","*/bar"})
+	@Swap(value=SwapUon.class, mediaTypes={"*/foo","*/uon","*/bar"})
+	@Swap(value=SwapUrlEncoding.class, mediaTypes={"*/foo","*/x-www-form-urlencoded","*/bar"})
+	@Swap(value=SwapMsgPack.class, mediaTypes={"*/foo","*/msgpack","*/bar"})
+	@Swap(value=SwapRdfXml.class, mediaTypes={"*/foo","*/xml+rdf","*/bar"})
 	public static class TestMediaTypePatternsMulti {}
 
-	@Swaps(
-		{
-			@Swap(value=SwapJson.class, mediaTypes={"*/foo","*/json","*/bar"}),
-			@Swap(value=SwapXml.class, mediaTypes={"*/foo","*/xml","*/bar"}),
-			@Swap(value=SwapHtml.class, mediaTypes={"*/foo","*/html","*/bar"}),
-		}
-	)
+	@Swap(value=SwapJson.class, mediaTypes={"*/foo","*/json","*/bar"})
+	@Swap(value=SwapXml.class, mediaTypes={"*/foo","*/xml","*/bar"})
+	@Swap(value=SwapHtml.class, mediaTypes={"*/foo","*/html","*/bar"})
 	public static class TestMediaTypePatternsPartial1 {
 		@Override
 		public String toString() {
@@ -573,14 +553,10 @@ public class SwapsAnnotationComboTest extends ComboSerializeTest {
 		}
 	}
 
-	@Swaps(
-		{
-			@Swap(value=SwapUon.class, mediaTypes={"*/foo","*/uon","*/bar"}),
-			@Swap(value=SwapUrlEncoding.class, mediaTypes={"*/foo","*/x-www-form-urlencoded","*/bar"}),
-			@Swap(value=SwapMsgPack.class, mediaTypes={"*/foo","*/msgpack","*/bar"}),
-			@Swap(value=SwapRdfXml.class, mediaTypes={"*/foo","*/xml+rdf","*/bar"}),
-		}
-	)
+	@Swap(value=SwapUon.class, mediaTypes={"*/foo","*/uon","*/bar"})
+	@Swap(value=SwapUrlEncoding.class, mediaTypes={"*/foo","*/x-www-form-urlencoded","*/bar"})
+	@Swap(value=SwapMsgPack.class, mediaTypes={"*/foo","*/msgpack","*/bar"})
+	@Swap(value=SwapRdfXml.class, mediaTypes={"*/foo","*/xml+rdf","*/bar"})
 	public static class TestMediaTypePatternsPartial2 {
 		@Override
 		public String toString() {
@@ -588,11 +564,7 @@ public class SwapsAnnotationComboTest extends ComboSerializeTest {
 		}
 	}
 
-	@Swaps(
-		{
-			@Swap(value=SwapXml.class, mediaTypes={"text/xml+*"}),
-		}
-	)
+	@Swap(value=SwapXml.class, mediaTypes={"text/xml+*"})
 	public static class TestMediaTypePatternsXmlPlus {
 		@Override
 		public String toString() {
@@ -600,11 +572,7 @@ public class SwapsAnnotationComboTest extends ComboSerializeTest {
 		}
 	}
 
-	@Swaps(
-		{
-			@Swap(value=SwapXml.class, mediaTypes={"text/*+xml"}),
-		}
-	)
+	@Swap(value=SwapXml.class, mediaTypes={"text/*+xml"})
 	public static class TestMediaTypePatternsXmlPlusReversed {
 		@Override
 		public String toString() {
@@ -612,11 +580,7 @@ public class SwapsAnnotationComboTest extends ComboSerializeTest {
 		}
 	}
 
-	@Swaps(
-		{
-			@Swap(value=SwapXml.class, mediaTypes={"text/rdf+*"}),
-		}
-	)
+	@Swap(value=SwapXml.class, mediaTypes={"text/rdf+*"})
 	public static class TestMediaTypePatternsRdfPlus {
 		@Override
 		public String toString() {
@@ -670,17 +634,13 @@ public class SwapsAnnotationComboTest extends ComboSerializeTest {
 	@Swap(impl=TemplateSwap.class,template="foo")
 	public static class TestTemplate {}
 
-	@Swaps(
-		{
-			@Swap(value=TemplateSwap.class, mediaTypes={"*/json"}, template="JSON"),
-			@Swap(value=TemplateSwap.class, mediaTypes={"*/xml"}, template="XML"),
-			@Swap(value=TemplateSwap.class, mediaTypes={"*/html"}, template="HTML"),
-			@Swap(value=TemplateSwap.class, mediaTypes={"*/uon"}, template="UON"),
-			@Swap(value=TemplateSwap.class, mediaTypes={"*/x-www-form-urlencoded"}, template="URLENCODING"),
-			@Swap(value=TemplateSwap.class, mediaTypes={"*/msgpack"}, template="MSGPACK"),
-			@Swap(value=TemplateSwap.class, mediaTypes={"*/xml+rdf"}, template="RDFXML"),
-		}
-	)
+	@Swap(value=TemplateSwap.class, mediaTypes={"*/json"}, template="JSON")
+	@Swap(value=TemplateSwap.class, mediaTypes={"*/xml"}, template="XML")
+	@Swap(value=TemplateSwap.class, mediaTypes={"*/html"}, template="HTML")
+	@Swap(value=TemplateSwap.class, mediaTypes={"*/uon"}, template="UON")
+	@Swap(value=TemplateSwap.class, mediaTypes={"*/x-www-form-urlencoded"}, template="URLENCODING")
+	@Swap(value=TemplateSwap.class, mediaTypes={"*/msgpack"}, template="MSGPACK")
+	@Swap(value=TemplateSwap.class, mediaTypes={"*/xml+rdf"}, template="RDFXML")
 	public static class TestTemplates {}
 
 
@@ -691,17 +651,13 @@ public class SwapsAnnotationComboTest extends ComboSerializeTest {
 		}
 	}
 
-	@Swaps(
-		{
-			@Swap(TemplateSwapJson.class),
-			@Swap(TemplateSwapXml.class),
-			@Swap(TemplateSwapHtml.class),
-			@Swap(TemplateSwapUon.class),
-			@Swap(TemplateSwapUrlEncoding.class),
-			@Swap(TemplateSwapMsgPack.class),
-			@Swap(TemplateSwapRdfXml.class),
-		}
-	)
+	@Swap(TemplateSwapJson.class)
+	@Swap(TemplateSwapXml.class)
+	@Swap(TemplateSwapHtml.class)
+	@Swap(TemplateSwapUon.class)
+	@Swap(TemplateSwapUrlEncoding.class)
+	@Swap(TemplateSwapMsgPack.class)
+	@Swap(TemplateSwapRdfXml.class)
 	public static class TestProgrammaticTemplates {}
 
 	public static class TemplateSwapJson extends TemplateSwap {
@@ -890,25 +846,17 @@ public class SwapsAnnotationComboTest extends ComboSerializeTest {
 		}
 	}
 
-	@Swaps(
-		{
-			@Swap(value=BeanSwap.class, mediaTypes={"*/json"}),
-			@Swap(value=BeanSwap.class, mediaTypes={"*/xml"}),
-			@Swap(value=BeanSwap.class, mediaTypes={"*/html"}),
-		}
-	)
+	@Swap(value=BeanSwap.class, mediaTypes={"*/json"})
+	@Swap(value=BeanSwap.class, mediaTypes={"*/xml"})
+	@Swap(value=BeanSwap.class, mediaTypes={"*/html"})
 	public static class BeanA {
 		public int f = 1;
 	}
 
-	@Swaps(
-		{
-			@Swap(value=BeanSwap.class, mediaTypes={"*/uon"}),
-			@Swap(value=BeanSwap.class, mediaTypes={"*/x-www-form-urlencoded"}),
-			@Swap(value=BeanSwap.class, mediaTypes={"*/msgpack"}),
-			@Swap(value=BeanSwap.class, mediaTypes={"*/xml+rdf"}),
-		}
-	)
+	@Swap(value=BeanSwap.class, mediaTypes={"*/uon"})
+	@Swap(value=BeanSwap.class, mediaTypes={"*/x-www-form-urlencoded"})
+	@Swap(value=BeanSwap.class, mediaTypes={"*/msgpack"})
+	@Swap(value=BeanSwap.class, mediaTypes={"*/xml+rdf"})
 	public static class BeanB {
 		public int f = 1;
 	}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/uon/annotation/UonBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/uon/annotation/UonBuilder_Test.java
new file mode 100644
index 0000000..2b5d36e
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/uon/annotation/UonBuilder_Test.java
@@ -0,0 +1,114 @@
+// ***************************************************************************************************************************
+// * 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.uon.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class UonBuilder_Test {
+
+	private static final String CNAME = UonBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Uon a1 = UonBuilder.create()
+		.on("a")
+		.build();
+
+	Uon a2 = UonBuilder.create()
+		.on("a")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "on:['a'],"
+				+ "onClass:[]"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		Uon c1 = UonBuilder.create(C1.class).on(C2.class).build();
+		Uon c2 = UonBuilder.create("a").on("b").build();
+		Uon c3 = UonBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		Uon c4 = UonBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Uon(
+		on="a"
+	)
+	public static class D1 {}
+	Uon d1 = D1.class.getAnnotationsByType(Uon.class)[0];
+
+	@Uon(
+		on="a"
+	)
+	public static class D2 {}
+	Uon d2 = D2.class.getAnnotationsByType(Uon.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/urlencoding/annotation/UrlEncodingBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/urlencoding/annotation/UrlEncodingBuilder_Test.java
new file mode 100644
index 0000000..365410e
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/urlencoding/annotation/UrlEncodingBuilder_Test.java
@@ -0,0 +1,119 @@
+// ***************************************************************************************************************************
+// * 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.urlencoding.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class UrlEncodingBuilder_Test {
+
+	private static final String CNAME = UrlEncodingBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	UrlEncoding a1 = UrlEncodingBuilder.create()
+		.expandedParams(true)
+		.on("a")
+		.build();
+
+	UrlEncoding a2 = UrlEncodingBuilder.create()
+		.expandedParams(true)
+		.on("a")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "expandedParams:true,"
+				+ "on:['a'],"
+				+ "onClass:[]"
+			+ "}"
+		);
+	}
+
+	@Test
+	public void a02_testEquivalency() {
+		assertObject(a1).is(a2);
+		assertInteger(a1.hashCode()).is(a2.hashCode()).isNotAny(0,-1);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// PropertyStore equivalency.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test
+	public void b01_testEquivalencyInPropertyStores() {
+		BeanContext bc1 = BeanContext.create().annotations(a1).build();
+		BeanContext bc2 = BeanContext.create().annotations(a2).build();
+		assertTrue(bc1 == bc2);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Other methods.
+	//------------------------------------------------------------------------------------------------------------------
+
+	public static class C1 {
+		public int f1;
+		public void m1() {}
+	}
+	public static class C2 {
+		public int f2;
+		public void m2() {}
+	}
+
+	@Test
+	public void c01_otherMethods() throws Exception {
+		UrlEncoding c1 = UrlEncodingBuilder.create(C1.class).on(C2.class).build();
+		UrlEncoding c2 = UrlEncodingBuilder.create("a").on("b").build();
+		UrlEncoding c3 = UrlEncodingBuilder.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
+		UrlEncoding c4 = UrlEncodingBuilder.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
+
+		assertObject(c1).json().contains("on:['"+CNAME+"$C1','"+CNAME+"$C2']");
+		assertObject(c2).json().contains("on:['a','b']");
+		assertObject(c3).json().contains("on:['"+CNAME+"$C1.f1','"+CNAME+"$C2.f2']");
+		assertObject(c4).json().contains("on:['"+CNAME+"$C1.m1()','"+CNAME+"$C2.m2()']");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Comparison with declared annotations.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@UrlEncoding(
+		expandedParams=true,
+		on="a"
+	)
+	public static class D1 {}
+	UrlEncoding d1 = D1.class.getAnnotationsByType(UrlEncoding.class)[0];
+
+	@UrlEncoding(
+		expandedParams=true,
+		on="a"
+	)
+	public static class D2 {}
+	UrlEncoding d2 = D2.class.getAnnotationsByType(UrlEncoding.class)[0];
+
+	@Test
+	public void d01_comparisonWithDeclarativeAnnotations() {
+		assertObject(d1).is(d2).is(a1);
+		assertInteger(d1.hashCode()).is(d2.hashCode()).is(a1.hashCode()).isNotAny(0,-1);
+	}
+}
+
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/ArrayUtilsTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/ArrayUtilsTest.java
index a06636d..d8b16ca 100755
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/ArrayUtilsTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/ArrayUtilsTest.java
@@ -39,7 +39,7 @@ public class ArrayUtilsTest {
 	//====================================================================================================
 	@Test
 	public void testAppendArrayToArray() throws Exception {
-		String[] s = new String[0];
+		String[] s = {};
 
 		s = append(s, "a", "b");
 		assertObject(s).json().is("['a','b']");
@@ -62,7 +62,7 @@ public class ArrayUtilsTest {
 	//====================================================================================================
 	@Test
 	public void testAppendCollectionToArray() throws Exception {
-		String[] s = new String[0];
+		String[] s = {};
 
 		s = append(s, Arrays.asList(new String[]{"a","b"}));
 		assertObject(s).json().is("['a','b']");
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/XmlCollapsedTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/XmlCollapsedTest.java
index ba5ec38..b798106 100755
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/XmlCollapsedTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/XmlCollapsedTest.java
@@ -60,13 +60,13 @@ public class XmlCollapsedTest {
 		public List<String> f1 = new LinkedList<>();
 
 		@Xml(format=COLLAPSED)
-		public String[] f2 = new String[0];
+		public String[] f2 = {};
 
 		@Xml(format=COLLAPSED,childName="xf3")
 		public List<String> f3 = new LinkedList<>();
 
 		@Xml(format=COLLAPSED,childName="xf4")
-		public String[] f4 =  new String[0];
+		public String[] f4 =  {};
 	}
 
 	//====================================================================================================
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/annotation/XmlBuilder_Test.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/annotation/XmlBuilder_Test.java
new file mode 100644
index 0000000..5cfc505
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/xml/annotation/XmlBuilder_Test.java
@@ -0,0 +1,134 @@
+// ***************************************************************************************************************************
+// * 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.xml.annotation;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class XmlBuilder_Test {
+
+	private static final String CNAME = XmlBuilder_Test.class.getName();
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Basic tests
+	//------------------------------------------------------------------------------------------------------------------
+
+	Xml a1 = XmlBuilder.create()
+		.childName("a")
+		.format(XmlFormat.ATTR)
+		.namespace("c")
+		.on("d")
+		.prefix("e")
+		.build();
+
+	Xml a2 = XmlBuilder.create()
+		.childName("a")
+		.format(XmlFormat.ATTR)
+		.namespace("c")
+		.on("d")
+		.prefix("e")
+		.build();
+
+	@Test
+	public void a01_basic() {
+		assertObject(a1).json().is(""
+			+ "{"
+				+ "childName:'a',"
+				+ "format:'ATTR',"
+				+ "namespace:'c',"
+				+ "on:['d'],"
+				+ "onClass:[],"
+				+ "prefix:'e'"
+			+ "}"
... 42328 lines suppressed ...